Whamcloud - gitweb
LU-17431 nodemap: make dynamic nodemaps hierarchical 39/54739/18
authorSebastien Buisson <sbuisson@ddn.com>
Wed, 20 Mar 2024 14:40:56 +0000 (15:40 +0100)
committerOleg Drokin <green@whamcloud.com>
Thu, 10 Apr 2025 06:52:30 +0000 (06:52 +0000)
Introduce a new rn_subtree field in struct lu_nid_range.
This allows to have NID ranges included into other NID ranges,
hence creating a NID range hierarchy. NID ranges are created in
a subtree only if there is already a range in the parent tree
that includes it. Overlapping NID ranges remain forbidden.

Introduce new fields nm_subnodemaps, nm_parent_entry in struct
lu_nodemap to keep track of the sub-nodemaps attached to a given
nodemap. Also use a new nm_parent_nm field that points to the parent
nodemap. This allows to create a nodemap hierarchy, based on the
NID ranges.

Introduce a new -p|--parent option to lctl nodemap_add to specify
the parent nodemap of a dynamic nodemap at creation time. The NID
ranges of the child nodemap must be included in the ranges of the
parent nodemap. A dynamic nodemap can have only one parent.

Make a sub-nodemap inherit all the parent nodemap's properties,
and the id mapping as well. And expose parent name as part of nodemap
properties. This is valid for dynamic nodemaps only.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I04a14db92c068f3cb5b6b39e75feb802cebacd8a
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/54739
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Marc Vef <mvef@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/doc/lctl-nodemap-add.8
lustre/include/lustre_nodemap.h
lustre/ptlrpc/nodemap_handler.c
lustre/ptlrpc/nodemap_idmap.c
lustre/ptlrpc/nodemap_internal.h
lustre/ptlrpc/nodemap_lproc.c
lustre/ptlrpc/nodemap_range.c
lustre/ptlrpc/nodemap_storage.c
lustre/tests/sanity-sec.sh
lustre/utils/lctl.c
lustre/utils/obd.c

index 102f3f9..73e1b20 100644 (file)
@@ -6,6 +6,9 @@ lctl-nodemap_add \- create a new nodemap to define client behavior
 or
 .SY "lctl nodemap add"
 .RB [ -d | --dynamic ]
+.RB [ -p |
+.BI --parent " PARENT_NAME"
+]
 .BI --name " NODEMAP_NAME"
 .YS
 .SH DESCRIPTION
@@ -19,6 +22,10 @@ and filesystem access permission of those NID(s).
 Adds a temporary in-memory nodemap on the local node instead of a persistent
 one.
 .TP
+.BI -p ", " --parent " PARENT_NAME"
+Mandatory for dynamic nodemap. Specifies the parent nodemap to inherit
+properties from. This can be "default".
+.TP
 .BI --name " NODEMAP_NAME"
 The name to give the new nodemap. It can be any alphanumeric string of maximum
 length 16, except
index 643fa08..7a59954 100644 (file)
@@ -123,6 +123,12 @@ struct lu_nodemap {
        unsigned int             nm_offset_start_projid;
        /* number of values allocated to PROJID offset */
        unsigned int             nm_offset_limit_projid;
+       /* list of sub-nodemaps */
+       struct list_head         nm_subnodemaps;
+       /* list entry for parent nodemap */
+       struct list_head         nm_parent_entry;
+       /* link to parent nodemap */
+       struct lu_nodemap       *nm_parent_nm;
 };
 
 /* Store handles to local MGC storage to save config locally. In future
index adf474d..dcefca2 100644 (file)
@@ -35,6 +35,8 @@ bool nodemap_active;
 DEFINE_MUTEX(active_config_lock);
 struct nodemap_config *active_config;
 
+static int nodemap_copy_fileset(struct lu_nodemap *dst, struct lu_nodemap *src);
+
 /**
  * Nodemap destructor
  *
@@ -60,9 +62,17 @@ static void nodemap_destroy(struct lu_nodemap *nodemap)
 
        mutex_unlock(&active_config_lock);
 
+       if (nodemap->nm_parent_nm) {
+               list_del(&nodemap->nm_parent_entry);
+               nodemap_putref(nodemap->nm_parent_nm);
+       }
+
        if (!list_empty(&nodemap->nm_member_list))
                CWARN("nodemap_destroy failed to reclassify all members\n");
 
+       if (!list_empty(&nodemap->nm_subnodemaps))
+               CWARN("nodemap_destroy failed to reclassify all subnodemaps\n");
+
        nm_member_delete_list(nodemap);
 
        OBD_FREE_PTR(nodemap);
@@ -950,10 +960,11 @@ ssize_t nodemap_map_acl(struct lu_nodemap *nodemap, void *buf, size_t size,
 }
 EXPORT_SYMBOL(nodemap_map_acl);
 
-static void nodemap_inherit_properties(struct lu_nodemap *dst,
-                                      struct lu_nodemap *src,
-                                      bool is_new)
+static int nodemap_inherit_properties(struct lu_nodemap *dst,
+                                     struct lu_nodemap *src)
 {
+       int rc = 0;
+
        if (!src) {
                dst->nmf_trust_client_ids = 0;
                dst->nmf_allow_root_access = 0;
@@ -993,19 +1004,36 @@ static void nodemap_inherit_properties(struct lu_nodemap *dst,
                dst->nm_squash_uid = src->nm_squash_uid;
                dst->nm_squash_gid = src->nm_squash_gid;
                dst->nm_squash_projid = src->nm_squash_projid;
-               if (is_new) {
-                       dst->nm_sepol[0] = '\0';
-               }
                dst->nm_offset_start_uid = src->nm_offset_start_uid;
                dst->nm_offset_limit_uid = src->nm_offset_limit_uid;
                dst->nm_offset_start_gid = src->nm_offset_start_gid;
                dst->nm_offset_limit_gid = src->nm_offset_limit_gid;
                dst->nm_offset_start_projid = src->nm_offset_start_projid;
                dst->nm_offset_limit_projid = src->nm_offset_limit_projid;
-               /* filesets cannot yet be inherited */
-               dst->nm_prim_fileset = NULL;
-               dst->nm_prim_fileset_size = 0;
+               if (src->nm_id == LUSTRE_NODEMAP_DEFAULT_ID) {
+                       dst->nm_sepol[0] = '\0';
+               } else {
+                       /* because we are copying from an existing nodemap,
+                        * we already know this string is well formatted
+                        */
+                       strcpy(dst->nm_sepol, src->nm_sepol);
+                       rc = idmap_copy_tree(dst, src);
+                       if (rc)
+                               goto out;
+               }
+               /* only dynamic nodemap inherits fileset from parent */
+               if (dst->nm_dyn) {
+                       rc = nodemap_copy_fileset(dst, src);
+                       if (rc)
+                               goto out;
+               } else {
+                       dst->nm_prim_fileset = NULL;
+                       dst->nm_prim_fileset_size = 0;
+               }
        }
+
+out:
+       return rc;
 }
 
 /*
@@ -1024,6 +1052,7 @@ int nodemap_add_range_helper(struct nodemap_config *config,
                             const struct lnet_nid nid[2],
                             u8 netmask, unsigned int range_id)
 {
+       struct lu_nid_range *prange = NULL;
        struct lu_nid_range *range;
        int rc;
 
@@ -1035,7 +1064,7 @@ int nodemap_add_range_helper(struct nodemap_config *config,
                GOTO(out, rc = -ENOMEM);
        }
 
-       rc = range_insert(config, range);
+       rc = range_insert(config, range, &prange, nodemap->nm_dyn);
        if (rc) {
                CDEBUG_LIMIT(rc == -EEXIST ? D_INFO : D_ERROR,
                             "cannot insert nodemap range into '%s': rc = %d\n",
@@ -1046,6 +1075,32 @@ int nodemap_add_range_helper(struct nodemap_config *config,
                GOTO(out, rc);
        }
 
+       if (nodemap->nm_dyn) {
+               /* Verify that the parent already associated with the nodemap
+                * is the one the prange belongs to.
+                */
+               struct lu_nodemap *parent;
+
+               if (!nodemap->nm_parent_nm ||
+                   list_empty(&nodemap->nm_parent_entry)) {
+                       CDEBUG(D_INFO, "dynamic nodemap %s has no parent\n",
+                              nodemap->nm_name);
+                       GOTO(err_parent, rc = -EINVAL);
+               }
+               parent = prange ?
+                       prange->rn_nodemap : config->nmc_default_nodemap;
+               if (nodemap->nm_parent_nm != parent) {
+                       CDEBUG(D_INFO,
+                              "%s: range [%s-%s] is not included in range of parent nodemap %s\n",
+                              nodemap->nm_name,
+                              libcfs_nidstr(&nid[0]), libcfs_nidstr(&nid[1]),
+                              nodemap->nm_parent_nm->nm_name);
+err_parent:
+                       range_delete(config, range);
+                       up_write(&config->nmc_range_tree_lock);
+                       GOTO(out, rc = -EINVAL);
+               }
+       }
        list_add(&range->rn_list, &nodemap->nm_ranges);
 
        /* nodemaps have no members if they aren't on the active config */
@@ -1281,6 +1336,34 @@ static int nodemap_set_fileset_local(struct lu_nodemap *nodemap,
 }
 
 /**
+ * Copy a fileset from a source to a destination nodemap. This is a local,
+ * non-persistent operation made for dynamic nodemaps.
+ * *
+ * \param      dst             the nodemap to set fileset on
+ * \param      src             the nodemap to fetch fileset from
+ * \retval     0 on success
+ * \retval     < 0 on error
+ */
+static int nodemap_copy_fileset(struct lu_nodemap *dst, struct lu_nodemap *src)
+{
+       char *fileset;
+       int rc = 0;
+
+       fileset = nodemap_get_fileset(src);
+       if (!fileset) {
+               dst->nm_prim_fileset = NULL;
+               dst->nm_prim_fileset_size = 0;
+       } else {
+               /* nodemap_set_fileset_iam() knows how to
+                * handle a dynamic nodemap
+                */
+               rc = nodemap_set_fileset_iam(dst, fileset);
+       }
+
+       return rc;
+}
+
+/**
  * Set fileset on a named nodemap
  *
  * \param      name            name of the nodemap to set fileset on
@@ -1312,6 +1395,12 @@ int nodemap_set_fileset(const char *name, const char *fileset, bool checkperm,
                RETURN(PTR_ERR(nodemap));
        }
 
+       /* FIXME: for now the fileset on a dynamic nodemap can just be
+        * inherited from the parent, not set explicitly
+        */
+       if (nodemap->nm_dyn)
+               GOTO(out_unlock, rc = -EPERM);
+
        if (checkperm && !allow_op_on_nm(nodemap))
                GOTO(out_unlock, rc = -EPERM);
 
@@ -1483,18 +1572,51 @@ EXPORT_SYMBOL(nodemap_get_sepol);
  */
 struct lu_nodemap *nodemap_create(const char *name,
                                  struct nodemap_config *config,
-                                 bool is_default)
+                                 bool is_default, bool dynamic)
 {
        struct lu_nodemap *nodemap = NULL;
        struct lu_nodemap *default_nodemap;
-       struct lu_nodemap *parent_nodemap;
+       struct lu_nodemap *parent_nodemap = NULL;
        struct cfs_hash *hash = config->nmc_nodemap_hash;
+       char newname[LUSTRE_NODEMAP_NAME_LENGTH + 1];
        int rc = 0;
        ENTRY;
 
        default_nodemap = config->nmc_default_nodemap;
 
-       if (!nodemap_name_is_valid(name))
+       if (dynamic) {
+               char pname[LUSTRE_NODEMAP_NAME_LENGTH + 1];
+               char format[32];
+
+               /* for a dynamic nodemap, nodemap_name is in the form:
+                * parent_name/new_name
+                */
+               if (!strchr(name, '/'))
+                       GOTO(out, rc = -EINVAL);
+               rc = snprintf(format, sizeof(format), "%%%zu[^/]/%%%zus",
+                             sizeof(pname) - 1, sizeof(newname) - 1);
+               if (rc >= sizeof(format))
+                       GOTO(out, rc = -ENAMETOOLONG);
+               rc = sscanf(name, format, pname, newname);
+               if (rc != 2)
+                       GOTO(out, rc = -EINVAL);
+
+               if (!nodemap_name_is_valid(pname))
+                       GOTO(out, rc = -EINVAL);
+
+               /* the call to nodemap_create for a dynamic nodemap comes from
+                * nodemap_add, which holds the active_config_lock
+                */
+               parent_nodemap = nodemap_lookup(pname);
+               if (IS_ERR(parent_nodemap))
+                       GOTO(out, rc = PTR_ERR(parent_nodemap));
+       } else {
+               rc = snprintf(newname, sizeof(newname), "%s", name);
+               if (rc >= sizeof(newname))
+                       GOTO(out, rc = -ENAMETOOLONG);
+       }
+
+       if (!nodemap_name_is_valid(newname))
                GOTO(out, rc = -EINVAL);
 
        if (hash == NULL) {
@@ -1503,7 +1625,7 @@ struct lu_nodemap *nodemap_create(const char *name,
        }
 
        OBD_ALLOC_PTR(nodemap);
-       if (nodemap == NULL) {
+       if (!nodemap) {
                CERROR("cannot allocate memory (%zu bytes) for nodemap '%s'\n",
                       sizeof(*nodemap), name);
                GOTO(out, rc = -ENOMEM);
@@ -1514,25 +1636,40 @@ struct lu_nodemap *nodemap_create(const char *name,
         * while it's being created.
         */
        refcount_set(&nodemap->nm_refcount, 2);
-       snprintf(nodemap->nm_name, sizeof(nodemap->nm_name), "%s", name);
-       rc = cfs_hash_add_unique(hash, name, &nodemap->nm_hash);
-       if (rc != 0) {
-               OBD_FREE_PTR(nodemap);
+       snprintf(nodemap->nm_name, sizeof(nodemap->nm_name), "%s", newname);
+
+       nodemap->nm_fs_to_client_uidmap = RB_ROOT;
+       nodemap->nm_client_to_fs_uidmap = RB_ROOT;
+       nodemap->nm_fs_to_client_gidmap = RB_ROOT;
+       nodemap->nm_client_to_fs_gidmap = RB_ROOT;
+       nodemap->nm_fs_to_client_projidmap = RB_ROOT;
+       nodemap->nm_client_to_fs_projidmap = RB_ROOT;
+
+       nodemap->nm_dyn = dynamic;
+       if (!parent_nodemap)
+               rc = nodemap_inherit_properties(nodemap,
+                                          is_default ? NULL : default_nodemap);
+       else
+               rc = nodemap_inherit_properties(nodemap, parent_nodemap);
+       if (rc)
+               GOTO(out, rc);
+
+       rc = cfs_hash_add_unique(hash, newname, &nodemap->nm_hash);
+       if (rc)
                GOTO(out, rc = -EEXIST);
-       }
 
        INIT_LIST_HEAD(&nodemap->nm_ranges);
        INIT_LIST_HEAD(&nodemap->nm_list);
        INIT_LIST_HEAD(&nodemap->nm_member_list);
+       INIT_LIST_HEAD(&nodemap->nm_subnodemaps);
+       INIT_LIST_HEAD(&nodemap->nm_parent_entry);
+       nodemap->nm_parent_nm = parent_nodemap;
+       if (parent_nodemap)
+               list_add(&nodemap->nm_parent_entry,
+                        &parent_nodemap->nm_subnodemaps);
 
        mutex_init(&nodemap->nm_member_list_lock);
        init_rwsem(&nodemap->nm_idmap_lock);
-       nodemap->nm_fs_to_client_uidmap = RB_ROOT;
-       nodemap->nm_client_to_fs_uidmap = RB_ROOT;
-       nodemap->nm_fs_to_client_gidmap = RB_ROOT;
-       nodemap->nm_client_to_fs_gidmap = RB_ROOT;
-       nodemap->nm_fs_to_client_projidmap = RB_ROOT;
-       nodemap->nm_client_to_fs_projidmap = RB_ROOT;
 
        if (is_default) {
                nodemap->nm_id = LUSTRE_NODEMAP_DEFAULT_ID;
@@ -1546,12 +1683,12 @@ struct lu_nodemap *nodemap_create(const char *name,
                CWARN("adding nodemap '%s' to config without default nodemap\n",
                      nodemap->nm_name);
 
-       parent_nodemap = is_default ? NULL : default_nodemap;
-       nodemap_inherit_properties(nodemap, parent_nodemap, true);
-
        RETURN(nodemap);
 
 out:
+       OBD_FREE_PTR(nodemap);
+       if (!IS_ERR_OR_NULL(parent_nodemap))
+               nodemap_putref(parent_nodemap);
        CERROR("cannot add nodemap: '%s': rc = %d\n", name, rc);
        RETURN(ERR_PTR(rc));
 }
@@ -2006,12 +2143,11 @@ int nodemap_add(const char *nodemap_name, bool dynamic)
        int rc;
 
        mutex_lock(&active_config_lock);
-       nodemap = nodemap_create(nodemap_name, active_config, 0);
+       nodemap = nodemap_create(nodemap_name, active_config, 0, dynamic);
        if (IS_ERR(nodemap)) {
                mutex_unlock(&active_config_lock);
                return PTR_ERR(nodemap);
        }
-       nodemap->nm_dyn = dynamic;
 
        rc = nodemap_idx_nodemap_add(nodemap);
        if (rc == 0)
@@ -2052,6 +2188,22 @@ int nodemap_del(const char *nodemap_name)
                nodemap_putref(nodemap);
                GOTO(out, rc = -EPERM);
        }
+
+       /* delete sub-nodemaps first */
+       if (!list_empty(&nodemap->nm_subnodemaps)) {
+               struct lu_nodemap *nm, *nm_temp;
+
+               list_for_each_entry_safe(nm, nm_temp, &nodemap->nm_subnodemaps,
+                                        nm_parent_entry) {
+                       /* do our best and report any error on sub-nodemaps
+                        * but do not forward rc
+                        */
+                       rc2 = nodemap_del(nm->nm_name);
+                       CDEBUG_LIMIT(D_INFO,
+                                    "cannot del sub-nodemap %s: rc = %d\n",
+                                    nm->nm_name, rc2);
+               }
+       }
        nodemap_putref(nodemap);
 
        /* we had dropped lock, so fetch nodemap again */
@@ -2094,13 +2246,18 @@ int nodemap_del(const char *nodemap_name)
        lprocfs_nodemap_remove(nodemap->nm_pde_data);
        nodemap->nm_pde_data = NULL;
 
+       if (!list_empty(&nodemap->nm_subnodemaps))
+               CWARN("%s: nodemap_del failed to remove all subnodemaps\n",
+                     nodemap_name);
+
        /* reclassify all member exports from nodemap, so they put their refs */
        down_read(&active_config->nmc_range_tree_lock);
        nm_member_reclassify_nodemap(nodemap);
        up_read(&active_config->nmc_range_tree_lock);
 
        if (!list_empty(&nodemap->nm_member_list))
-               CWARN("nodemap_del failed to reclassify all members\n");
+               CWARN("%s: nodemap_del failed to reclassify all members\n",
+                     nodemap_name);
 
        mutex_unlock(&active_config_lock);
        nodemap_putref(nodemap);
@@ -2524,7 +2681,7 @@ int nodemap_mod_init(void)
                GOTO(out, rc = PTR_ERR(new_config));
        }
 
-       nodemap = nodemap_create(DEFAULT_NODEMAP, new_config, 1);
+       nodemap = nodemap_create(DEFAULT_NODEMAP, new_config, 1, false);
        if (IS_ERR(nodemap)) {
                nodemap_config_dealloc(new_config);
                nodemap_procfs_exit();
index afcfedf..b31fc8c 100644 (file)
@@ -49,7 +49,7 @@ static void idmap_destroy(struct lu_idmap *idmap)
 /**
  * Insert idmap into the proper trees
  *
- * \param      id_type         NODEMAP_UID or NODEMAP_GID
+ * \param      id_type         NODEMAP_UID or NODEMAP_GID or NODEMAP_PROJID
  * \param      idmap           lu_idmap structure to insert
  * \param      nodemap         nodemap to associate with the map
  *
@@ -278,3 +278,76 @@ void idmap_delete_tree(struct lu_nodemap *nodemap)
                idmap_destroy(idmap);
        }
 }
+
+/*
+ * copy all idmap trees from a source nodemap to a dest nodemap
+ *
+ * \param      dst             nodemap to copy trees to
+ * \param      src             nodemap to copy trees from
+ *
+ * \retval     0 on success, error code otherwise
+ *
+ * This uses the postorder safe traversal code that is committed
+ * in a later kernel. Each lu_idmap structure is copied.
+ * No need for this function to hold nm_idmap_lock, as it is called
+ * only when a sub-nodemap is first attached to a parent.
+ */
+int idmap_copy_tree(struct lu_nodemap *dst, struct lu_nodemap *src)
+{
+       struct lu_idmap *idmap, *temp, *idmap_new, *err;
+       struct rb_root root;
+       int rc = 0;
+
+       root = src->nm_fs_to_client_uidmap;
+       rbtree_postorder_for_each_entry_safe(idmap, temp, &root,
+                                            id_fs_to_client) {
+               idmap_new = idmap_create(idmap->id_client, idmap->id_fs);
+               if (!idmap_new)
+                       GOTO(out_copy_tree, rc = -ENOMEM);
+
+               err = idmap_insert(NODEMAP_UID, idmap_new, dst);
+               if (err) {
+                       OBD_FREE_PTR(idmap);
+                       GOTO(out_copy_tree,
+                            rc = IS_ERR(err) ? PTR_ERR(err) : -EEXIST);
+               }
+       }
+
+       root = src->nm_client_to_fs_gidmap;
+       rbtree_postorder_for_each_entry_safe(idmap, temp, &root,
+                                            id_client_to_fs) {
+               idmap_new = idmap_create(idmap->id_client, idmap->id_fs);
+               if (!idmap_new)
+                       GOTO(out_copy_tree, rc = -ENOMEM);
+
+               err = idmap_insert(NODEMAP_GID, idmap_new, dst);
+               if (err) {
+                       OBD_FREE_PTR(idmap);
+                       GOTO(out_copy_tree,
+                            rc = IS_ERR(err) ? PTR_ERR(err) : -EEXIST);
+               }
+       }
+
+       root = src->nm_client_to_fs_projidmap;
+       rbtree_postorder_for_each_entry_safe(idmap, temp, &root,
+                                            id_client_to_fs) {
+               idmap_new = idmap_create(idmap->id_client, idmap->id_fs);
+               if (!idmap_new)
+                       GOTO(out_copy_tree, rc = -ENOMEM);
+
+               err = idmap_insert(NODEMAP_PROJID, idmap_new, dst);
+               if (err) {
+                       OBD_FREE_PTR(idmap);
+                       GOTO(out_copy_tree,
+                            rc = IS_ERR(err) ? PTR_ERR(err) : -EEXIST);
+               }
+       }
+
+out_copy_tree:
+       if (rc)
+               CDEBUG(D_INFO,
+                      "Copying idmap %d:%d from %s to %s failed: rc=%d\n",
+                      idmap->id_client, idmap->id_fs,
+                      src->nm_name, dst->nm_name, rc);
+       return rc;
+}
index 65b5d8c..2abb180 100644 (file)
@@ -53,6 +53,8 @@ struct lu_nid_range {
         */
        struct list_head         rn_nidlist;
        struct rb_node           rn_rb;
+       /* sub ranges included in this NID range */
+       struct nodemap_range_tree rn_subtree;
 };
 
 struct lu_idmap {
@@ -101,7 +103,7 @@ static inline __u32 nm_idx_set_type(unsigned int id, enum nodemap_idx_type t)
 void nodemap_config_set_active(struct nodemap_config *config);
 struct lu_nodemap *nodemap_create(const char *name,
                                  struct nodemap_config *config,
-                                 bool is_default);
+                                 bool is_default, bool dynamic);
 void nodemap_putref(struct lu_nodemap *nodemap);
 struct lu_nodemap *nodemap_lookup(const char *name);
 
@@ -116,7 +118,8 @@ struct lu_nid_range *range_create(struct nodemap_config *config,
                                  u8 netmask, struct lu_nodemap *nodemap,
                                  unsigned int range_id);
 void range_destroy(struct lu_nid_range *range);
-int range_insert(struct nodemap_config *config, struct lu_nid_range *data);
+int range_insert(struct nodemap_config *config, struct lu_nid_range *range,
+                struct lu_nid_range **parent_range, bool dynamic);
 void range_delete(struct nodemap_config *config, struct lu_nid_range *data);
 struct lu_nid_range *range_search(struct nodemap_config *config,
                                  struct lnet_nid *nid);
@@ -132,6 +135,7 @@ struct lu_idmap *idmap_insert(enum nodemap_id_type id_type,
 void idmap_delete(enum nodemap_id_type id_type,  struct lu_idmap *idmap,
                  struct lu_nodemap *nodemap);
 void idmap_delete_tree(struct lu_nodemap *nodemap);
+int idmap_copy_tree(struct lu_nodemap *dst, struct lu_nodemap *src);
 struct lu_idmap *idmap_search(struct lu_nodemap *nodemap,
                              enum nodemap_tree_type,
                              enum nodemap_id_type id_type,
index 33a89bc..138a6b3 100644 (file)
@@ -844,6 +844,43 @@ static int nodemap_deny_mount_seq_show(struct seq_file *m, void *data)
        return 0;
 }
 
+/**
+ * Reads and prints the name of the parent nodemap for the given nodemap.
+ *
+ * \param      seq             seq file in proc fs
+ * \param      data            unused
+ * \retval     0               success
+ */
+static int nodemap_parent_seq_show(struct seq_file *seq, void *data)
+{
+       struct lu_nodemap *nodemap;
+       char *pname;
+       int rc;
+
+       mutex_lock(&active_config_lock);
+       nodemap = nodemap_lookup(seq->private);
+       mutex_unlock(&active_config_lock);
+       if (IS_ERR(nodemap)) {
+               rc = PTR_ERR(nodemap);
+               CERROR("cannot find nodemap '%s': rc = %d\n",
+                      (char *)seq->private, rc);
+               return rc;
+       }
+
+       if (nodemap->nm_dyn) {
+               if (nodemap->nm_parent_nm)
+                       pname = nodemap->nm_parent_nm->nm_name;
+               else
+                       pname = DEFAULT_NODEMAP;
+       } else {
+               pname = "";
+       }
+
+       seq_printf(seq, "%s\n", pname);
+       nodemap_putref(nodemap);
+       return 0;
+}
+
 static struct ldebugfs_vars lprocfs_nm_module_vars[] = {
        {
                .name           = "active",
@@ -868,6 +905,7 @@ LDEBUGFS_SEQ_FOPS_RO(nodemap_audit_mode);
 LDEBUGFS_SEQ_FOPS_RO(nodemap_forbid_encryption);
 LDEBUGFS_SEQ_FOPS_RO(nodemap_readonly_mount);
 LDEBUGFS_SEQ_FOPS_RO(nodemap_deny_mount);
+LDEBUGFS_SEQ_FOPS_RO(nodemap_parent);
 
 static const struct file_operations nodemap_ranges_fops = {
        .open           = nodemap_ranges_open,
@@ -933,6 +971,10 @@ static struct ldebugfs_vars lprocfs_nodemap_vars[] = {
                .fops           = &nodemap_map_mode_fops,
        },
        {
+               .name           = "parent",
+               .fops           = &nodemap_parent_fops,
+       },
+       {
                .name           = "ranges",
                .fops           = &nodemap_ranges_fops,
        },
index 647bc31..5fb1477 100644 (file)
 INTERVAL_TREE_DEFINE(struct lu_nid_range, rn_rb, lnet_nid_t, rn_subtree_last,
                     START, LAST, static, nm_range)
 
+static int __range_is_included(lnet_nid_t needle_start, lnet_nid_t needle_end,
+                              struct lu_nid_range *haystack)
+{
+       return LNET_NIDADDR(START(haystack)) <= LNET_NIDADDR(needle_start) &&
+              LNET_NIDADDR(LAST(haystack)) >= LNET_NIDADDR(needle_end);
+}
+
+static int range_is_included(struct lu_nid_range *needle,
+                            struct lu_nid_range *haystack)
+{
+       return __range_is_included(START(needle), LAST(needle), haystack);
+}
+
 /*
  * range constructor
  *
@@ -145,6 +158,7 @@ struct lu_nid_range *range_create(struct nodemap_config *config,
        INIT_LIST_HEAD(&range->rn_nidlist);
        if (!list_empty(&tmp_nidlist))
                list_splice(&tmp_nidlist, &range->rn_nidlist);
+       range->rn_subtree.nmrt_range_interval_root = INTERVAL_TREE_ROOT;
 
        return range;
 }
@@ -172,10 +186,15 @@ struct lu_nid_range *__range_find(struct nodemap_range_tree *nm_range_tree,
 
        range = nm_range_iter_first(&nm_range_tree->nmrt_range_interval_root,
                                    nid4[0], nid4[1]);
-       while (range &&
-              (!nid_same(&range->rn_start, start_nid) ||
-               !nid_same(&range->rn_end, end_nid)))
+       while (range) {
+               if (nid_same(&range->rn_start, start_nid) &&
+                   nid_same(&range->rn_end, end_nid))
+                       break;
+               if (__range_is_included(nid4[0], nid4[1], range))
+                       return __range_find(&range->rn_subtree,
+                                           start_nid, end_nid);
                range = nm_range_iter_next(range, nid4[0], nid4[1]);
+       }
 
        return range;
 }
@@ -233,24 +252,44 @@ void range_destroy(struct lu_nid_range *range)
  * to exactly one range
  */
 static int __range_insert(struct nodemap_range_tree *nm_range_tree,
-                         struct lu_nid_range *range)
+                         struct lu_nid_range *range,
+                         struct lu_nid_range **parent_range, bool dynamic)
 {
-       if (nm_range_iter_first(&nm_range_tree->nmrt_range_interval_root,
-                               lnet_nid_to_nid4(&range->rn_start),
-                               lnet_nid_to_nid4(&range->rn_end)))
-               return -EEXIST;
+       struct lu_nid_range *found = NULL;
+       int rc = 0;
+
+       found = nm_range_iter_first(&nm_range_tree->nmrt_range_interval_root,
+                                   lnet_nid_to_nid4(&range->rn_start),
+                                   lnet_nid_to_nid4(&range->rn_end));
+       if (found) {
+               if (dynamic && range_is_included(range, found)) {
+                       rc = __range_insert(&found->rn_subtree,
+                                           range, parent_range, dynamic);
+                       if (!rc) {
+                               if (parent_range && !*parent_range)
+                                       *parent_range = found;
+                       }
+               } else {
+                       rc = -EEXIST;
+               }
+               GOTO(out_insert, rc);
+       }
 
        nm_range_insert(range,
                        &nm_range_tree->nmrt_range_interval_root);
-       return 0;
+
+out_insert:
+       return rc;
 }
 
-int range_insert(struct nodemap_config *config, struct lu_nid_range *range)
+int range_insert(struct nodemap_config *config, struct lu_nid_range *range,
+                struct lu_nid_range **parent_range, bool dynamic)
 {
        int rc = 0;
 
        if (!range->rn_netmask) {
-               rc = __range_insert(&config->nmc_range_tree, range);
+               rc = __range_insert(&config->nmc_range_tree,
+                                   range, parent_range, dynamic);
        } else {
                if (range_find(config, &range->rn_start, &range->rn_end,
                               range->rn_netmask))
@@ -271,8 +310,28 @@ int range_insert(struct nodemap_config *config, struct lu_nid_range *range)
 static void __range_delete(struct nodemap_range_tree *nm_range_tree,
                           struct lu_nid_range *range)
 {
-       nm_range_remove(range,
-                       &nm_range_tree->nmrt_range_interval_root);
+       struct lu_nid_range *found;
+       lnet_nid_t nid4[2];
+
+       nid4[0] = lnet_nid_to_nid4(&range->rn_start);
+       nid4[1] = lnet_nid_to_nid4(&range->rn_end);
+
+       found = nm_range_iter_first(&nm_range_tree->nmrt_range_interval_root,
+                                   nid4[0], nid4[1]);
+       while (found) {
+               if (nid_same(&found->rn_start, &range->rn_start) &&
+                   nid_same(&found->rn_end, &range->rn_end))
+                       break;
+               if (__range_is_included(nid4[0], nid4[1], found)) {
+                       __range_delete(&found->rn_subtree, range);
+                       return;
+               }
+               found = nm_range_iter_next(found, nid4[0], nid4[1]);
+       }
+
+       if (found)
+               nm_range_remove(found,
+                               &nm_range_tree->nmrt_range_interval_root);
 }
 
 void range_delete(struct nodemap_config *config, struct lu_nid_range *range)
@@ -296,9 +355,18 @@ static
 struct lu_nid_range *__range_search(struct nodemap_range_tree *nm_range_tree,
                                    struct lnet_nid *nid)
 {
-       return nm_range_iter_first(&nm_range_tree->nmrt_range_interval_root,
-                                  lnet_nid_to_nid4(nid),
-                                  lnet_nid_to_nid4(nid));
+       struct lu_nid_range *range, *subrange;
+
+       range = nm_range_iter_first(&nm_range_tree->nmrt_range_interval_root,
+                                   lnet_nid_to_nid4(nid),
+                                   lnet_nid_to_nid4(nid));
+       if (range) {
+               subrange = __range_search(&range->rn_subtree, nid);
+               if (subrange)
+                       range = subrange;
+       }
+
+       return range;
 }
 
 struct lu_nid_range *range_search(struct nodemap_config *config,
index 906600e..e88bd27 100644 (file)
@@ -966,10 +966,10 @@ int nodemap_idx_fileset_add(const struct lu_nodemap *nodemap,
 
        ENTRY;
 
-       if (!nodemap_mgs()) {
-               if (nodemap->nm_dyn)
-                       return 0;
+       if (nodemap->nm_dyn)
+               return 0;
 
+       if (!nodemap_mgs()) {
                rc = -EINVAL;
                CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
                       nodemap->nm_name, rc);
@@ -1038,10 +1038,10 @@ int nodemap_idx_fileset_update(const struct lu_nodemap *nodemap,
 
        ENTRY;
 
-       if (!nodemap_mgs()) {
-               if (nodemap->nm_dyn)
-                       return 0;
+       if (nodemap->nm_dyn)
+               return 0;
 
+       if (!nodemap_mgs()) {
                rc = -EINVAL;
                CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
                       nodemap->nm_name, rc);
@@ -1089,10 +1089,10 @@ int nodemap_idx_fileset_del(const struct lu_nodemap *nodemap,
 
        ENTRY;
 
-       if (!nodemap_mgs()) {
-               if (nodemap->nm_dyn)
-                       return 0;
+       if (nodemap->nm_dyn)
+               return 0;
 
+       if (!nodemap_mgs()) {
                rc = -EINVAL;
                CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
                       nodemap->nm_name, rc);
@@ -1154,10 +1154,10 @@ int nodemap_idx_fileset_clear(const struct lu_nodemap *nodemap)
 
        ENTRY;
 
-       if (!nodemap_mgs()) {
-               if (nodemap->nm_dyn)
-                       return 0;
+       if (nodemap->nm_dyn)
+               return 0;
 
+       if (!nodemap_mgs()) {
                rc = -EINVAL;
                CERROR("%s: cannot add nodemap config to non-existing MGS: rc = %d\n",
                       nodemap->nm_name, rc);
@@ -1370,9 +1370,11 @@ static int nodemap_cluster_rec_helper(struct nodemap_config *config,
        nodemap = cfs_hash_lookup(config->nmc_nodemap_hash, rec->ncr.ncr_name);
        if (nodemap == NULL) {
                if (nodemap_id == LUSTRE_NODEMAP_DEFAULT_ID)
-                       nodemap = nodemap_create(rec->ncr.ncr_name, config, 1);
+                       nodemap = nodemap_create(rec->ncr.ncr_name, config, 1,
+                                                false);
                else
-                       nodemap = nodemap_create(rec->ncr.ncr_name, config, 0);
+                       nodemap = nodemap_create(rec->ncr.ncr_name, config, 0,
+                                                false);
                if (IS_ERR(nodemap))
                        return PTR_ERR(nodemap);
 
@@ -1831,7 +1833,7 @@ out:
        if (new_config->nmc_default_nodemap == NULL) {
                /* new MGS won't have a default nm on disk, so create it here */
                struct lu_nodemap *nodemap =
-                       nodemap_create(DEFAULT_NODEMAP, new_config, 1);
+                       nodemap_create(DEFAULT_NODEMAP, new_config, 1, false);
                if (IS_ERR(nodemap)) {
                        rc = PTR_ERR(nodemap);
                } else {
index 98d18c8..7e4f145 100755 (executable)
@@ -7109,6 +7109,12 @@ test_72() {
        local nids=1.1.1.[1-100]@tcp
        local startnid=1.1.1.1@tcp
        local endnid=1.1.1.100@tcp
+       local subnids1=1.1.1.[2-50]@tcp
+       local subnids2=1.1.1.[51-100]@tcp
+       local subnids3=1.1.1.[2-25]@tcp
+       local subnids4=1.1.1.[51-52]@tcp
+       local subnids5=1.1.1.[26-60]@tcp
+       local subnids6=1.1.1.[1-60]@tcp
        local clid=500
        local fsid=1000
        local properties="audit_mode deny_unknown forbid_encryption \
@@ -7130,6 +7136,9 @@ test_72() {
                stack_trap cleanup_active EXIT
        fi
 
+       do_facet mgs $LCTL nodemap_set_fileset --name default \
+               --fileset "/deffset" ||
+                       error "setting fileset on default failed"
        do_facet mgs $LCTL nodemap_add $mgsnm ||
                error "adding $mgsnm on MGS failed"
        stack_trap "do_facet mgs $LCTL nodemap_del $mgsnm" EXIT
@@ -7145,10 +7154,12 @@ test_72() {
        stack_trap "do_facet ost1 $LCTL nodemap_del $nm || true" EXIT
        do_facet ost1 $LCTL nodemap_add $nm &&
                error "static nodemap on server should fail"
-       do_facet ost1 $LCTL nodemap_add -d $nm ||
+       do_facet ost1 $LCTL nodemap_add -d $nm &&
+               error "dynamic nodemap without parent should fail"
+       do_facet ost1 $LCTL nodemap_add -d -p default $nm ||
                error "dynamic nodemap on server failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.id)
-       if [[ "x$val" == "x" ]] || [[ "x$val" == "x0" ]]; then
+       if [[ -z "$val" || "$val" == "0" ]]; then
                error "dynamic nodemap wrong id $val"
        fi
 
@@ -7156,148 +7167,174 @@ test_72() {
                error "dynamic add_range on server failed"
        val=$(do_facet ost1 $LCTL get_param nodemap.$nm.ranges |
                awk 'BEGIN{RS=", "} $1=="start_nid:"{print $2 ; exit}')
-       if [[ "x$val" != "x$startnid" ]]; then
+       [[ "$val" == "$startnid" ]] ||
                error "dynamic nodemap wrong start nid range $val"
-       fi
        val=$(do_facet ost1 $LCTL get_param nodemap.$nm.ranges |
                awk 'BEGIN{RS=", "} $1=="end_nid:"{print $2 ; exit}')
-       if [[ "x$val" != "x$endnid" ]]; then
+       [[ "$val" == "$endnid" ]] ||
                error "dynamic nodemap wrong end nid range $val"
-       fi
 
        do_facet ost1 $LCTL nodemap_add_idmap --name $nm --idtype uid \
                --idmap $clid:$fsid ||
                        error "dynamic add_idmap on server failed"
        val=$(do_facet ost1 $LCTL get_param nodemap.$nm.idmap |
                awk 'BEGIN{RS=", "} $1=="client_id:"{print $2 ; exit}')
-       if [[ "x$val" != "x$clid" ]]; then
-               error "dynamic nodemap wrong client id $val"
-       fi
+       (( val == clid )) || error "dynamic nodemap wrong client id $val"
        val=$(do_facet ost1 $LCTL get_param nodemap.$nm.idmap |
                awk 'BEGIN{RS=", "} $1=="fs_id:"{print $2 ; exit}')
-       if [[ "x$val" != "x$fsid" ]]; then
-               error "dynamic nodemap wrong fs id $val"
-       fi
+       (( val == fsid )) || error "dynamic nodemap wrong fs id $val"
 
        for prop in $properties; do
                do_facet ost1 $LCTL nodemap_modify --name $nm \
                        --property $prop --value 1 ||
                                error "dynamic modify of $prop failed"
                val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-               [[ "x$val" == "x1" ]] || error "incorrect $prop $val"
+               (( val == 1 )) || error "incorrect $prop $val"
        done
        prop=admin
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value 1 ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.admin_nodemap)
-       [[ "x$val" == "x1" ]] || error "incorrect $prop $val"
+       (( val == 1 )) || error "incorrect $prop $val"
        prop=trusted
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value 0 ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.trusted_nodemap)
-       [[ "x$val" == "x0" ]] || error "incorrect $prop $val"
+       (( val == 0 )) || error "incorrect $prop $val"
        prop=map_mode
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value uid ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "xuid" ]] || error "incorrect $prop $val"
+       [[ "$val" == "uid" ]] || error "incorrect $prop $val"
        prop=rbac
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value file_perms ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "xfile_perms" ]] || error "incorrect $prop $val"
+       [[ "$val" == "file_perms" ]] || error "incorrect $prop $val"
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value all ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "x$rbac_val" ]] || error "incorrect $prop $val"
+       [[ "$val" == "$rbac_val" ]] || error "incorrect $prop $val"
        prop=squash_uid
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value 77 ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "x77" ]] || error "incorrect $prop $val"
+       (( val == 77 )) || error "incorrect $prop $val"
        prop=squash_gid
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value 77 ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "x77" ]] || error "incorrect $prop $val"
+       (( val == 77 )) || error "incorrect $prop $val"
        prop=squash_projid
        do_facet ost1 $LCTL nodemap_modify --name $nm \
                --property $prop --value 77 ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "x77" ]] || error "incorrect $prop $val"
+       (( val == 77 )) || error "incorrect $prop $val"
        prop=fileset
        do_facet ost1 $LCTL nodemap_set_fileset --name $nm \
-               --fileset "/tmp" ||
-                       error "dynamic modify of $prop failed"
+               --fileset "/tmp" &&
+                       error "dynamic modify of $prop should fail"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "x/tmp" ]] || error "incorrect $prop $val"
+       [[ "$val" == "/deffset" ]] || error "incorrect $prop $val"
        prop=sepol
        do_facet ost1 $LCTL nodemap_set_sepol --name $nm \
                --sepol $sepol ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop)
-       [[ "x$val" == "x$sepol" ]] || error "incorrect $prop $val"
+       [[ "$val" == "$sepol" ]] || error "incorrect $prop $val"
+
        prop=offset
        do_facet ost1 $LCTL nodemap_add_offset --name $nm \
                --offset 100000 --limit 200000 ||
                        error "dynamic modify of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop |
                awk '$1 == "start_uid:" {print $2}' | sed s+,++)
-       [[ "x$val" == "x100000" ]] || error "incorrect $prop start_uid $val"
+       (( val == 100000 )) || error "incorrect $prop start_uid $val"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop |
                awk '$1 == "limit_uid:" {print $2}' | sed s+,++)
-       [[ "x$val" == "x200000" ]] || error "incorrect $prop limit_uid $val"
+       (( val == 200000 )) || error "incorrect $prop limit_uid $val"
        do_facet ost1 $LCTL nodemap_del_offset --name $nm ||
                        error "dynamic del of $prop failed"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop |
                awk '$1 == "start_uid:" {print $2}' | sed s+,++)
-       [[ "x$val" == "x0" ]] || error "incorrect $prop start_uid $val"
+       (( val == 0 )) || error "incorrect $prop start_uid $val"
        val=$(do_facet ost1 $LCTL get_param -n nodemap.$nm.$prop |
                awk '$1 == "limit_uid:" {print $2}' | sed s+,++)
-       [[ "x$val" == "x0" ]] || error "incorrect $prop limit_uid $val"
+       (( val == 0 )) || error "incorrect $prop limit_uid $val"
 
        val=$(do_facet ost1 $LCTL nodemap_test_id --nid $startnid \
                --idtype uid --id $clid)
-       if [[ "x$val" != "x$fsid" ]]; then
-               error "dynamic test_id on server failed"
-       fi
+       (( val == fsid )) || error "dynamic test_id on server failed"
 
        do_facet ost1 $LCTL nodemap_del_idmap --name $nm --idtype uid \
                --idmap $clid:$fsid ||
                        error "dynamic del_idmap on server failed"
        val=$(do_facet ost1 $LCTL get_param nodemap.$nm.idmap |
                awk 'BEGIN{RS=", "} $1=="client_id:"{print $2 ; exit}')
-       if [[ "x$val" != "x" ]]; then
-               error "idmap should be empty, got $val"
-       fi
+       [[ -z "$val" ]] || error "idmap should be empty, got $val"
 
        val=$(do_facet ost1 $LCTL nodemap_test_nid $startnid)
-       if [[ "x$val" != "x$nm" ]]; then
-               error "dynamic test_nid on server failed"
-       fi
+       [[ "$val" == "$nm" ]] || error "dynamic test_nid on server failed"
+
+       do_facet ost1 $LCTL nodemap_add -d -p $nm ${nm}_1 ||
+               error "nodemap add ${nm}_1 on server failed"
+       stack_trap "do_facet ost1 $LCTL nodemap_del ${nm}_1 || true" EXIT
+       do_facet ost1 $LCTL nodemap_add_range --name ${nm}_1 \
+               --range $subnids1 ||
+                       error "add_range for ${nm}_1 failed"
+       val=$(do_facet ost1 $LCTL get_param -n nodemap.${nm}_1.parent)
+       [[ "$val" == "$nm" ]] ||
+               error "parent of ${nm}_1 should be $nm, got $val"
+
+       do_facet ost1 $LCTL nodemap_add -d -p $nm ${nm}_2 ||
+               error "nodemap add ${nm}_2 on server failed"
+       stack_trap "do_facet ost1 $LCTL nodemap_del ${nm}_2 || true" EXIT
+       do_facet ost1 $LCTL nodemap_add_range --name ${nm}_2 \
+               --range $subnids2 ||
+                       error "add_range for ${nm}_2 failed"
+       val=$(do_facet ost1 $LCTL get_param -n nodemap.${nm}_2.parent)
+       [[ "$val" == "$nm" ]] ||
+               error "parent of ${nm}_2 should be $nm, got $val"
+
+       do_facet ost1 $LCTL nodemap_add -d -p ${nm}_1 ${nm}_3 ||
+               error "nodemap add ${nm}_3 on server failed"
+       stack_trap "do_facet ost1 $LCTL nodemap_del ${nm}_3 || true" EXIT
+       do_facet ost1 $LCTL nodemap_add_range --name ${nm}_3 \
+               --range $subnids4 &&
+                      error "nodemap ${nm}_3 should not accept range $subnids4"
+       do_facet ost1 $LCTL nodemap_add_range --name ${nm}_3 \
+               --range $subnids5 &&
+                      error "nodemap ${nm}_3 should not accept range $subnids5"
+       do_facet ost1 $LCTL nodemap_add_range --name ${nm}_3 \
+               --range $subnids6 &&
+                      error "nodemap ${nm}_3 should not accept range $subnids6"
+       do_facet ost1 $LCTL nodemap_add_range --name ${nm}_3 \
+               --range $subnids3 ||
+                       error "add_range $subnids3 for ${nm}_3 failed"
+       val=$(do_facet ost1 $LCTL get_param -n nodemap.${nm}_3.parent)
+       [[ "$val" == "${nm}_1" ]] ||
+               error "parent of ${nm}_3 should be ${nm}_1, got $val"
+       val=$(do_facet ost1 $LCTL get_param -n nodemap.${nm}_3.squash_projid)
+       (( val == 77 )) || error "squash_projid should be inherited, got $val"
 
        do_facet ost1 $LCTL nodemap_del_range --name $nm --range $nids ||
                error "dynamic del_range on server failed"
        val=$(do_facet ost1 $LCTL get_param nodemap.$nm.ranges |
                awk 'BEGIN{RS=", "} $1=="start_nid:"{print $2 ; exit}')
-       if [[ "x$val" != "x" ]]; then
-               error "nid range should be empty, got $val"
-       fi
+       [[ -z "$val" ]] || error "nid range should be empty, got $val"
 
        do_facet ost1 $LCTL nodemap_del $nm ||
                error "dynamic nodemap del on server failed"
        val=$(do_facet ost1 $LCTL get_param nodemap.$nm.id)
-       if [[ "x$val" != "x" ]]; then
-               error "nodemap should be gone, got $val"
-       fi
+       [[ -z "$val" ]] || error "nodemap should be gone, got $val"
 
        do_facet ost1 $LCTL nodemap_add_range --name $mgsnm --range $mgsnids2 &&
                        error "add_range $mgsnm on server should fail"
index 7551f77..79beba7 100644 (file)
@@ -166,7 +166,7 @@ command_t nodemap_cmdlist[] = {
         "usage: nodemap activate {0|1}"},
        {.pc_name = "add", .pc_func = jt_nodemap_add,
         .pc_help = "add a new nodemap\n"
-        "usage: nodemap add [-d|--dynamic] --name NODEMAP_NAME"},
+        "usage: nodemap add [-d|--dynamic] [-p|--parent PARENT_NAME] --name NODEMAP_NAME"},
        {.pc_name = "del", .pc_func = jt_nodemap_del,
         .pc_help = "remove a nodemap\n"
         "usage: nodemap del --name NODEMAP_NAME"},
@@ -591,7 +591,7 @@ command_t cmdlist[] = {
         "usage: nodemap_activate {0|1}"},
        {"nodemap_add", jt_nodemap_add, 0,
         "add a new nodemap\n"
-        "usage: nodemap_add [-d|--dynamic] --name NODEMAP_NAME"},
+        "usage: nodemap_add [-d|--dynamic] [-p|--parent PARENT_NAME] --name NODEMAP_NAME"},
        {"nodemap_del", jt_nodemap_del, 0,
         "remove a nodemap\n"
         "usage: nodemap_del --name NODEMAP_NAME"},
index f131f81..aed7f26 100644 (file)
@@ -78,6 +78,7 @@
 #include <linux/lustre/lustre_ver.h>
 
 #include <lustre/lustreapi.h>
+#include <uapi/linux/lustre/lustre_disk.h>
 
 #define MAX_STRING_SIZE 128
 
@@ -4023,7 +4024,8 @@ int jt_nodemap_activate(int argc, char **argv)
  */
 int jt_nodemap_add(int argc, char **argv)
 {
-       char *nodemap_name = NULL;
+       char nm_to_send[LUSTRE_NODEMAP_NAME_LENGTH*2 + 2];
+       char *nodemap_name = NULL, *parent_nm = NULL;
        bool dynamic = false;
        int c, rc = EXIT_SUCCESS;
 
@@ -4031,9 +4033,10 @@ int jt_nodemap_add(int argc, char **argv)
                { .val = 'd', .name = "dynamic", .has_arg = no_argument },
                { .val = 'h', .name = "help",    .has_arg = no_argument },
                { .val = 'n', .name = "name",    .has_arg = required_argument },
+               { .val = 'p', .name = "parent",  .has_arg = required_argument },
                { .name = NULL } };
 
-       while ((c = getopt_long(argc, argv, "dhn:",
+       while ((c = getopt_long(argc, argv, "dhn:p:",
                                long_opts, NULL)) != -1) {
                switch (c) {
                case 'd':
@@ -4042,6 +4045,9 @@ int jt_nodemap_add(int argc, char **argv)
                case 'n':
                        nodemap_name = optarg;
                        break;
+               case 'p':
+                       parent_nm = optarg;
+                       break;
                case 'h':
                default:
                        return CMD_HELP;
@@ -4056,21 +4062,54 @@ int jt_nodemap_add(int argc, char **argv)
                nodemap_name = argv[optind];
        }
 
-       if (!dynamic && !is_mgs()) {
+       if (dynamic && !parent_nm) {
                fprintf(stderr,
-                       "nodemap_add: non-dynamic nodemap only allowed on MGS node\n");
+                       "nodemap_add: missing parent for dynamic nodemap\n");
                return CMD_HELP;
        }
 
-       if (llapi_nodemap_exists(nodemap_name) == 0) {
+       if (!dynamic) {
+               if (!is_mgs()) {
+                       fprintf(stderr,
+                               "nodemap_add: non-dynamic nodemap only allowed on MGS node\n");
+                       return CMD_HELP;
+               }
+               if (parent_nm) {
+                       fprintf(stderr,
+                               "nodemap_add: invalid parent for non-dynamic nodemap\n");
+                       return CMD_HELP;
+               }
+       }
+
+       if (!llapi_nodemap_exists(nodemap_name)) {
                fprintf(stderr, "error: nodemap '%s' already exists\n",
                        nodemap_name);
                errno = EINVAL;
                goto out;
        }
 
+       if (parent_nm) {
+               if (llapi_nodemap_exists(parent_nm)) {
+                       fprintf(stderr, "error: parent '%s' does not exist\n",
+                               parent_nm);
+                       errno = EINVAL;
+                       goto out;
+               }
+       }
+
+       if (snprintf(nm_to_send, sizeof(nm_to_send), "%s%s%s",
+                    parent_nm ? parent_nm : "",
+                    parent_nm ? "/" : "", nodemap_name) >=
+           sizeof(nm_to_send)) {
+               fprintf(stderr, "error: nodemap names %s%s%s too long\n",
+                       parent_nm ? parent_nm : "", parent_nm ? "/" : "",
+                       nodemap_name);
+               errno = EINVAL;
+               goto out;
+       }
+
        errno = -nodemap_cmd(LCFG_NODEMAP_ADD, dynamic, NULL, 0, argv[0],
-                            nodemap_name, NULL);
+                            nm_to_send, NULL);
 
 out:
        if (errno) {