return 0;
 }
 
+/*********************** methods for rbd_group_directory ***********************/
+
+int dir_add(cls_method_context_t hctx,
+            const string &name, const string &id,
+            bool check_for_unique_id)
+{
+  if (!name.size() || !is_valid_id(id)) {
+    CLS_ERR("invalid group name '%s' or id '%s'",
+            name.c_str(), id.c_str());
+    return -EINVAL;
+  }
+
+  CLS_LOG(20, "dir_add name=%s id=%s", name.c_str(), id.c_str());
+
+  string name_key = dir_key_for_name(name);
+  string id_key = dir_key_for_id(id);
+  string tmp;
+  int r = read_key(hctx, name_key, &tmp);
+  if (r != -ENOENT) {
+    CLS_LOG(10, "name already exists");
+    return -EEXIST;
+  }
+  r = read_key(hctx, id_key, &tmp);
+  if (r != -ENOENT && check_for_unique_id) {
+    CLS_LOG(10, "id already exists");
+    return -EBADF;
+  }
+  bufferlist id_bl, name_bl;
+  encode(id, id_bl);
+  encode(name, name_bl);
+  map<string, bufferlist> omap_vals;
+  omap_vals[name_key] = id_bl;
+  omap_vals[id_key] = name_bl;
+  return cls_cxx_map_set_vals(hctx, &omap_vals);
+}
+
+int dir_remove(cls_method_context_t hctx,
+               const string &name, const string &id)
+{
+  CLS_LOG(20, "dir_remove name=%s id=%s", name.c_str(), id.c_str());
+
+  string name_key = dir_key_for_name(name);
+  string id_key = dir_key_for_id(id);
+  string stored_name, stored_id;
+
+  int r = read_key(hctx, name_key, &stored_id);
+  if (r < 0) {
+    if (r != -ENOENT)
+      CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+  r = read_key(hctx, id_key, &stored_name);
+  if (r < 0) {
+    if (r != -ENOENT)
+      CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
+  // check if this op raced with a rename
+  if (stored_name != name || stored_id != id) {
+    CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'",
+            stored_name.c_str(), stored_id.c_str(), name.c_str(), id.c_str());
+    return -ESTALE;
+  }
+
+  r = cls_cxx_map_remove_key(hctx, name_key);
+  if (r < 0) {
+    CLS_ERR("error removing name: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
+  r = cls_cxx_map_remove_key(hctx, id_key);
+  if (r < 0) {
+    CLS_ERR("error removing id: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
+  return 0;
+}
+
 /**
  * List groups from the directory.
  *
     return -EINVAL;
   }
 
-  if (!name.size() || !is_valid_id(id)) {
-    CLS_ERR("invalid group name '%s' or id '%s'",
-           name.c_str(), id.c_str());
+  return dir_add(hctx, name, id, true);
+}
+
+/**
+ * Rename a group to the directory.
+ *
+ * Input:
+ * @param src original name of the group (std::string)
+ * @param dest new name of the group (std::string)
+ * @param id the id of the group (std::string)
+ *
+ * Output:
+ * @return 0 on success, negative error code on failure
+ */
+int group_dir_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+  string src, dest, id;
+  try {
+    bufferlist::iterator iter = in->begin();
+    decode(src, iter);
+    decode(dest, iter);
+    decode(id, iter);
+  } catch (const buffer::error &err) {
     return -EINVAL;
   }
 
-  CLS_LOG(20, "group_dir_add name=%s id=%s", name.c_str(), id.c_str());
+  int r = dir_remove(hctx, src, id);
+  if (r < 0)
+    return r;
 
-  string tmp;
-  string name_key = dir_key_for_name(name);
-  string id_key = dir_key_for_id(id);
-  r = read_key(hctx, name_key, &tmp);
-  if (r != -ENOENT) {
-    CLS_LOG(10, "name already exists");
-    return -EEXIST;
-  }
-  r = read_key(hctx, id_key, &tmp);
-  if (r != -ENOENT) {
-    CLS_LOG(10, "id already exists");
-    return -EBADF;
-  }
-  bufferlist id_bl, name_bl;
-  encode(id, id_bl);
-  encode(name, name_bl);
-  map<string, bufferlist> omap_vals;
-  omap_vals[name_key] = id_bl;
-  omap_vals[id_key] = name_bl;
-  return cls_cxx_map_set_vals(hctx, &omap_vals);
+  return dir_add(hctx, dest, id, false);
 }
 
 /**
     return -EINVAL;
   }
 
-  CLS_LOG(20, "group_dir_remove name=%s id=%s", name.c_str(), id.c_str());
-
-  string stored_name, stored_id;
-  string name_key = dir_key_for_name(name);
-  string id_key = dir_key_for_id(id);
-
-  int r = read_key(hctx, name_key, &stored_id);
-  if (r < 0) {
-    if (r != -ENOENT)
-      CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r).c_str());
-    return r;
-  }
-  r = read_key(hctx, id_key, &stored_name);
-  if (r < 0) {
-    if (r != -ENOENT)
-      CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r).c_str());
-    return r;
-  }
-
-  // check if this op raced with a rename
-  if (stored_name != name || stored_id != id) {
-    CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'",
-           stored_name.c_str(), stored_id.c_str(), name.c_str(), id.c_str());
-    return -ESTALE;
-  }
-
-  r = cls_cxx_map_remove_key(hctx, name_key);
-  if (r < 0) {
-    CLS_ERR("error removing name: %s", cpp_strerror(r).c_str());
-    return r;
-  }
-
-  r = cls_cxx_map_remove_key(hctx, id_key);
-  if (r < 0) {
-    CLS_ERR("error removing id: %s", cpp_strerror(r).c_str());
-    return r;
-  }
-
-  return 0;
+  return dir_remove(hctx, name, id);
 }
 
 /**
   cls_method_handle_t h_group_dir_list;
   cls_method_handle_t h_group_dir_add;
   cls_method_handle_t h_group_dir_remove;
+  cls_method_handle_t h_group_dir_rename;
   cls_method_handle_t h_group_image_remove;
   cls_method_handle_t h_group_image_list;
   cls_method_handle_t h_group_image_set;
   cls_register_cxx_method(h_class, "group_dir_remove",
                          CLS_METHOD_RD | CLS_METHOD_WR,
                          group_dir_remove, &h_group_dir_remove);
+  cls_register_cxx_method(h_class, "group_dir_rename",
+                          CLS_METHOD_RD | CLS_METHOD_WR,
+                          group_dir_rename, &h_group_dir_rename);
   cls_register_cxx_method(h_class, "group_image_remove",
                          CLS_METHOD_RD | CLS_METHOD_WR,
                          group_image_remove, &h_group_image_remove);
 
 
     // Consistency groups functions
     int group_dir_list(librados::IoCtx *ioctx, const std::string &oid,
-                    const std::string &start, uint64_t max_return,
-                    map<string, string> *cgs)
+                       const std::string &start, uint64_t max_return,
+                       map<string, string> *cgs)
     {
       bufferlist in, out;
       encode(start, in);
     }
 
     int group_dir_add(librados::IoCtx *ioctx, const std::string &oid,
-                  const std::string &name, const std::string &id)
+                      const std::string &name, const std::string &id)
     {
       bufferlist in, out;
       encode(name, in);
       return ioctx->exec(oid, "rbd", "group_dir_add", in, out);
     }
 
+    int group_dir_rename(librados::IoCtx *ioctx, const std::string &oid,
+                         const std::string &src, const std::string &dest,
+                         const std::string &id)
+    {
+      bufferlist in, out;
+      encode(src, in);
+      encode(dest, in);
+      encode(id, in);
+      return ioctx->exec(oid, "rbd", "group_dir_rename", in, out);
+    }
+
     int group_dir_remove(librados::IoCtx *ioctx, const std::string &oid,
-                     const std::string &name, const std::string &id)
+                         const std::string &name, const std::string &id)
     {
       bufferlist in, out;
       encode(name, in);
 
 
     // Consistency groups functions
     int group_dir_list(librados::IoCtx *ioctx, const std::string &oid,
-                   const std::string &start, uint64_t max_return,
-                   map<string, string> *groups);
+                       const std::string &start, uint64_t max_return,
+                       map<string, string> *groups);
     int group_dir_add(librados::IoCtx *ioctx, const std::string &oid,
-                  const std::string &name, const std::string &id);
+                      const std::string &name, const std::string &id);
+    int group_dir_rename(librados::IoCtx *ioctx, const std::string &oid,
+                         const std::string &src, const std::string &dest,
+                         const std::string &id);
     int group_dir_remove(librados::IoCtx *ioctx, const std::string &oid,
-                     const std::string &name, const std::string &id);
+                         const std::string &name, const std::string &id);
     int group_image_remove(librados::IoCtx *ioctx, const std::string &oid,
                           const cls::rbd::GroupImageSpec &spec);
     int group_image_list(librados::IoCtx *ioctx, const std::string &oid,
 
   ASSERT_EQ(-EEXIST, group_dir_add(&ioctx, RBD_GROUP_DIRECTORY, group_name, group_id));
 }
 
+TEST_F(TestClsRbd, group_dir_rename) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+  ioctx.remove(RBD_GROUP_DIRECTORY);
+
+  string group_id = "cgid";
+  string src_name = "cgnamesrc";
+  string dest_name = "cgnamedest";
+  add_group_to_dir(ioctx, group_id, src_name);
+
+  ASSERT_EQ(0, group_dir_rename(&ioctx, RBD_GROUP_DIRECTORY,
+                                src_name, dest_name, group_id));
+  map<string, string> cgs;
+  ASSERT_EQ(0, group_dir_list(&ioctx, RBD_GROUP_DIRECTORY, "", 10, &cgs));
+  ASSERT_EQ(1U, cgs.size());
+  auto it = cgs.begin();
+  ASSERT_EQ(group_id, it->second);
+  ASSERT_EQ(dest_name, it->first);
+
+  // destination group name existing
+  ASSERT_EQ(-EEXIST, group_dir_rename(&ioctx, RBD_GROUP_DIRECTORY,
+                                      dest_name, dest_name, group_id));
+  ASSERT_EQ(0, group_dir_remove(&ioctx, RBD_GROUP_DIRECTORY, dest_name, group_id));
+  // source group name missing
+  ASSERT_EQ(-ENOENT, group_dir_rename(&ioctx, RBD_GROUP_DIRECTORY,
+                                      dest_name, src_name, group_id));
+}
+
 TEST_F(TestClsRbd, group_dir_remove) {
   librados::IoCtx ioctx;
   ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));