]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cls_rbd: Add group snapshot operations to cls_rbd
authorVictor Denisov <denisovenator@gmail.com>
Wed, 28 Sep 2016 01:01:00 +0000 (18:01 -0700)
committerJason Dillaman <dillaman@redhat.com>
Thu, 11 Jan 2018 15:38:23 +0000 (10:38 -0500)
Signed-off-by: Victor Denisov <denisovenator@gmail.com>
src/cls/rbd/cls_rbd.cc
src/cls/rbd/cls_rbd.h
src/cls/rbd/cls_rbd_client.cc
src/cls/rbd/cls_rbd_client.h
src/cls/rbd/cls_rbd_types.cc
src/cls/rbd/cls_rbd_types.h
src/test/cls_rbd/test_cls_rbd.cc
src/test/encoding/types.h

index 08f06985c29c9a3ca0ba5f0db128ac3054862674..94a5d8cf1fa8f0496a542cc0419eedc1cbfdf9c8 100644 (file)
@@ -110,8 +110,7 @@ static void key_from_snap_id(snapid_t snap_id, string *out)
   *out = oss.str();
 }
 
-static snapid_t snap_id_from_key(const string &key)
-{
+static snapid_t snap_id_from_key(const string &key) {
   istringstream iss(key);
   uint64_t id;
   iss.ignore(strlen(RBD_SNAP_KEY_PREFIX)) >> std::hex >> id;
@@ -4920,8 +4919,9 @@ int group_image_list(cls_method_context_t hctx,
   std::vector<cls::rbd::GroupImageStatus> res;
   bool more;
   do {
-    int r = cls_cxx_map_get_vals(hctx, last_read,cls::rbd::RBD_GROUP_IMAGE_KEY_PREFIX,
-                                 max_read, &vals, &more);
+    int r = cls_cxx_map_get_vals(hctx, last_read,
+                                    cls::rbd::RBD_GROUP_IMAGE_KEY_PREFIX,
+                                    max_read, &vals, &more);
     if (r < 0)
       return r;
 
@@ -5097,6 +5097,294 @@ int image_get_group(cls_method_context_t hctx,
   return 0;
 }
 
+
+namespace group {
+
+static int group_snap_list(cls_method_context_t hctx,
+                          cls::rbd::GroupSnapshot start_after,
+                          uint64_t max_return,
+                          std::vector<cls::rbd::GroupSnapshot> *group_snaps)
+{
+  int max_read = RBD_MAX_KEYS_READ;
+  std::map<string, bufferlist> vals;
+  string last_read = snap_key(start_after.id);
+
+  group_snaps->clear();
+
+  bool more;
+  do {
+    int r = cls_cxx_map_get_vals(hctx, last_read,
+                                    RBD_GROUP_SNAP_KEY_PREFIX,
+                                    max_read, &vals, &more);
+    if (r < 0)
+      return r;
+
+    for (map<string, bufferlist>::iterator it = vals.begin();
+        it != vals.end() && group_snaps->size() < max_return; ++it) {
+
+      bufferlist::iterator iter = it->second.begin();
+      cls::rbd::GroupSnapshot snap;
+      try {
+       ::decode(snap, iter);
+      } catch (const buffer::error &err) {
+       CLS_ERR("error decoding snapshot: %s", it->first.c_str());
+       return -EIO;
+      }
+      CLS_LOG(20, "Discovered snapshot %s %s",
+             snap.name.c_str(),
+             snap.id.c_str());
+      group_snaps->push_back(snap);
+    }
+
+  } while (more && (group_snaps->size() < max_return));
+
+  return 0;
+}
+
+static int check_duplicate_snap_name(cls_method_context_t hctx,
+                                    std::string snap_name,
+                                    std::string snap_id)
+{
+  const int max_read = 1024;
+  cls::rbd::GroupSnapshot snap_last;
+  std::vector<cls::rbd::GroupSnapshot> page;
+
+  for (;;) {
+    int r = group_snap_list(hctx, snap_last, max_read, &page);
+    if (r < 0) {
+      return r;
+    }
+    for (auto snap: page) {
+      if (snap.name == snap_name && snap.id != snap_id) {
+       return -EEXIST;
+      }
+    }
+
+    if (page.size() < max_read) {
+      break;
+    }
+
+    snap_last = *page.rbegin();
+  }
+
+  return 0;
+}
+
+static int check_duplicate_snap_id(cls_method_context_t hctx,
+                                  std::string snap_key)
+{
+  bufferlist bl;
+  int r = cls_cxx_map_get_val(hctx, snap_key, &bl);
+  if (r == -ENOENT) {
+    return 0;
+  } else {
+    return -EEXIST;
+  }
+}
+
+}
+
+/**
+ * Save initial snapshot record.
+ *
+ * Input:
+ * @param GroupSnapshot
+ *
+ * Output:
+ * @return 0 on success, negative error code on failure
+ */
+int group_snap_add(cls_method_context_t hctx,
+                  bufferlist *in, bufferlist *out)
+{
+  CLS_LOG(20, "group_snap_add");
+  cls::rbd::GroupSnapshot group_snap;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(group_snap, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+  if (group_snap.name.empty()) {
+    CLS_ERR("group snapshot name is empty");
+    return -EINVAL;
+  }
+  if (group_snap.id.empty()) {
+    CLS_ERR("group snapshot id is empty");
+    return -EINVAL;
+  }
+
+  int r = group::check_duplicate_snap_name(hctx, group_snap.name, group_snap.id);
+  if (r < 0) {
+    return r;
+  }
+
+  r = group::check_duplicate_snap_id(hctx, group::snap_key(group_snap.id));
+  if (r < 0) {
+    return r;
+  }
+
+  std::string key = group::snap_key(group_snap.id);
+
+  bufferlist obl;
+  ::encode(group_snap, obl);
+  r = cls_cxx_map_set_val(hctx, key, &obl);
+  return r;
+}
+
+/**
+ * Update snapshot record.
+ *
+ * Input:
+ * @param GroupSnapshot
+ *
+ * Output:
+ * @return 0 on success, negative error code on failure
+ */
+int group_snap_update(cls_method_context_t hctx,
+                     bufferlist *in, bufferlist *out)
+{
+  CLS_LOG(20, "group_snap_update");
+  cls::rbd::GroupSnapshot group_snap;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(group_snap, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+  if (group_snap.name.empty()) {
+    CLS_ERR("group snapshot name is empty");
+    return -EINVAL;
+  }
+
+  int r = group::check_duplicate_snap_name(hctx, group_snap.name, group_snap.id);
+  if (r < 0) {
+    return r;
+  }
+
+  if (group_snap.id.empty()) {
+    CLS_ERR("group snapshot id is empty");
+    return -EINVAL;
+  }
+
+  std::string key = group::snap_key(group_snap.id);
+
+  bufferlist obl;
+  ::encode(group_snap, obl);
+  r = cls_cxx_map_set_val(hctx, key, &obl);
+
+  return r;
+}
+
+/**
+ * Remove snapshot record.
+ *
+ * Input:
+ * @param id Snapshot id
+ *
+ * Output:
+ * @return 0 on success, negative error code on failure
+ */
+int group_snap_remove(cls_method_context_t hctx,
+                     bufferlist *in, bufferlist *out)
+{
+  CLS_LOG(20, "group_snap_remove");
+  std::string snap_id;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(snap_id, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  cls::rbd::GroupSnapshot group_snap;
+  group_snap.id = snap_id;
+  std::string snap_key = group::snap_key(group_snap.id);
+
+  CLS_LOG(20, "removing snapshot with key %s", snap_key.c_str());
+  int r = cls_cxx_map_remove_key(hctx, snap_key);
+  return r;
+}
+
+/**
+ * Get consistency group's snapshot by id.
+ *
+ * Input:
+ * @param snapshot_id the id of the snapshot to look for.
+ *
+ * Output:
+ * @param GroupSnapshot the requested snapshot
+ * @return 0 on success, negative error code on failure
+ */
+int group_snap_get_by_id(cls_method_context_t hctx,
+                        bufferlist *in, bufferlist *out)
+{
+  CLS_LOG(20, "group_snap_get_by_id");
+
+  std::string snap_id;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(snap_id, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  cls::rbd::GroupSnapshot group_snap;
+  group_snap.id = snap_id;
+
+  bufferlist snapbl;
+
+  int r = cls_cxx_map_get_val(hctx, group::snap_key(group_snap.id), &snapbl);
+  if (r < 0) {
+    return r;
+  }
+
+  bufferlist::iterator iter = snapbl.begin();
+  try {
+    ::decode(group_snap, iter);
+  } catch (const buffer::error &err) {
+    CLS_ERR("error decoding snapshot: %s", snap_id.c_str());
+    return -EIO;
+  }
+
+  ::encode(group_snap, *out);
+
+  return 0;
+}
+
+/**
+ * List consistency group's snapshots.
+ *
+ * Input:
+ * @param start_after which name to begin listing after
+ *       (use the empty string to start at the beginning)
+ * @param max_return the maximum number of snapshots to list
+ *
+ * Output:
+ * @param list of snapshots
+ * @return 0 on success, negative error code on failure
+ */
+int group_snap_list(cls_method_context_t hctx,
+                   bufferlist *in, bufferlist *out)
+{
+  CLS_LOG(20, "group_snap_list");
+
+  cls::rbd::GroupSnapshot start_after;
+  uint64_t max_return;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(start_after, iter);
+    ::decode(max_return, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+  std::vector<cls::rbd::GroupSnapshot> group_snaps;
+  group::group_snap_list(hctx, start_after, max_return, &group_snaps);
+
+  ::encode(group_snaps, *out);
+
+  return 0;
+}
+
 namespace trash {
 
 static const std::string IMAGE_KEY_PREFIX("id_");
@@ -5291,7 +5579,6 @@ int trash_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
   } catch (const buffer::error &err) {
     return -EINVAL;
   }
-
   CLS_LOG(20, "trash_get_image id=%s", id.c_str());
 
 
@@ -5399,6 +5686,11 @@ CLS_INIT(rbd)
   cls_method_handle_t h_image_add_group;
   cls_method_handle_t h_image_remove_group;
   cls_method_handle_t h_image_get_group;
+  cls_method_handle_t h_group_snap_add;
+  cls_method_handle_t h_group_snap_update;
+  cls_method_handle_t h_group_snap_remove;
+  cls_method_handle_t h_group_snap_get_by_id;
+  cls_method_handle_t h_group_snap_list;
   cls_method_handle_t h_trash_add;
   cls_method_handle_t h_trash_remove;
   cls_method_handle_t h_trash_list;
@@ -5650,9 +5942,6 @@ CLS_INIT(rbd)
                           CLS_METHOD_WR, mirror_image_map_remove,
                           &h_mirror_image_map_remove);
   /* methods for the consistency groups feature */
-  cls_register_cxx_method(h_class, "group_create",
-                         CLS_METHOD_RD | CLS_METHOD_WR,
-                         group_create, &h_group_create);
   cls_register_cxx_method(h_class, "group_dir_list",
                          CLS_METHOD_RD,
                          group_dir_list, &h_group_dir_list);
@@ -5680,6 +5969,21 @@ CLS_INIT(rbd)
   cls_register_cxx_method(h_class, "image_get_group",
                          CLS_METHOD_RD,
                          image_get_group, &h_image_get_group);
+  cls_register_cxx_method(h_class, "group_snap_add",
+                         CLS_METHOD_RD | CLS_METHOD_WR,
+                         group_snap_add, &h_group_snap_add);
+  cls_register_cxx_method(h_class, "group_snap_update",
+                         CLS_METHOD_RD | CLS_METHOD_WR,
+                         group_snap_update, &h_group_snap_update);
+  cls_register_cxx_method(h_class, "group_snap_remove",
+                         CLS_METHOD_RD | CLS_METHOD_WR,
+                         group_snap_remove, &h_group_snap_remove);
+  cls_register_cxx_method(h_class, "group_snap_get_by_id",
+                         CLS_METHOD_RD,
+                         group_snap_get_by_id, &h_group_snap_get_by_id);
+  cls_register_cxx_method(h_class, "group_snap_list",
+                         CLS_METHOD_RD,
+                         group_snap_list, &h_group_snap_list);
 
   /* rbd_trash object methods */
   cls_register_cxx_method(h_class, "trash_add",
index f08fc1b1f0a65861e10cde4c48668a7040f79637..cac28a4a3d544dc94f92ead782076cfce2b17e4e 100644 (file)
@@ -164,4 +164,15 @@ struct cls_rbd_snap {
 };
 WRITE_CLASS_ENCODER(cls_rbd_snap)
 
+namespace group {
+
+  static const string RBD_GROUP_SNAP_KEY_PREFIX = "snapshot_";
+
+  std::string snap_key(std::string snap_id) {
+    ostringstream oss;
+    oss << RBD_GROUP_SNAP_KEY_PREFIX << snap_id;
+    return oss.str();
+  }
+}
+
 #endif
index 90c25c6bcc2956fb0a6e737231cae1efdf932275..b41428cec3c55bc54595195639791987c1efd3f3 100644 (file)
@@ -1932,13 +1932,6 @@ namespace librbd {
     }
 
     // Consistency groups functions
-    int group_create(librados::IoCtx *ioctx, const std::string &oid)
-    {
-      bufferlist bl, bl2;
-
-      return ioctx->exec(oid, "rbd", "group_create", bl, bl2);
-    }
-
     int group_dir_list(librados::IoCtx *ioctx, const std::string &oid,
                     const std::string &start, uint64_t max_return,
                     map<string, string> *cgs)
@@ -2071,6 +2064,81 @@ namespace librbd {
       return image_get_group_finish(&iter, group_spec);
     }
 
+    int group_snap_add(librados::IoCtx *ioctx, const std::string &oid,
+                      const cls::rbd::GroupSnapshot &snapshot)
+    {
+
+      bufferlist inbl, outbl;
+
+      ::encode(snapshot, inbl);
+      int r = ioctx->exec(oid, "rbd", "group_snap_add", inbl, outbl);
+      return r;
+    }
+
+    int group_snap_update(librados::IoCtx *ioctx, const std::string &oid,
+                         const cls::rbd::GroupSnapshot &snapshot)
+    {
+
+      bufferlist inbl, outbl;
+
+      ::encode(snapshot, inbl);
+      int r = ioctx->exec(oid, "rbd", "group_snap_update", inbl, outbl);
+      return r;
+    }
+
+    int group_snap_remove(librados::IoCtx *ioctx, const std::string &oid,
+                         const std::string &snap_id)
+    {
+      bufferlist inbl, outbl;
+      ::encode(snap_id, inbl);
+
+      return ioctx->exec(oid, "rbd", "group_snap_remove", inbl, outbl);
+    }
+
+    int group_snap_get_by_id(librados::IoCtx *ioctx, const std::string &oid,
+                            const std::string &snap_id,
+                            cls::rbd::GroupSnapshot *snapshot)
+    {
+      bufferlist inbl, outbl;
+      ::encode(snap_id, inbl);
+
+      int r = ioctx->exec(oid, "rbd", "group_snap_get_by_id", inbl, outbl);
+      if (r < 0) {
+       return r;
+      }
+
+      bufferlist::iterator iter = outbl.begin();
+      try {
+       ::decode(*snapshot, iter);
+      } catch (const buffer::error &err) {
+       return -EBADMSG;
+      }
+
+      return 0;
+    }
+    int group_snap_list(librados::IoCtx *ioctx, const std::string &oid,
+                       const cls::rbd::GroupSnapshot &start,
+                       uint64_t max_return,
+                       std::vector<cls::rbd::GroupSnapshot> *snapshots)
+    {
+      bufferlist inbl, outbl;
+      ::encode(start, inbl);
+      ::encode(max_return, inbl);
+
+      int r = ioctx->exec(oid, "rbd", "group_snap_list", inbl, outbl);
+      if (r < 0) {
+       return r;
+      }
+      bufferlist::iterator iter = outbl.begin();
+      try {
+       ::decode(*snapshots, iter);
+      } catch (const buffer::error &err) {
+       return -EBADMSG;
+      }
+
+      return 0;
+    }
+
     // rbd_trash functions
     void trash_add(librados::ObjectWriteOperation *op,
                   const std::string &id,
@@ -2129,7 +2197,6 @@ namespace librbd {
 
       return 0;
     }
-
     int trash_list(librados::IoCtx *ioctx,
                    const std::string &start, uint64_t max_return,
                    map<string, cls::rbd::TrashImageSpec> *entries)
@@ -2142,7 +2209,6 @@ namespace librbd {
       if (r < 0) {
        return r;
       }
-
       bufferlist::iterator iter = out_bl.begin();
       return trash_list_finish(&iter, entries);
     }
@@ -2167,7 +2233,6 @@ namespace librbd {
       return 0;
     }
 
-
     int trash_get(librados::IoCtx *ioctx, const std::string &id,
                   cls::rbd::TrashImageSpec *trash_spec)
     {
index dd0b27d52bd8c350db60aa9e0d77cca926033a2a..6a9bd04046a56d1f6c29e66960fabb716b6749ce 100644 (file)
@@ -399,7 +399,6 @@ namespace librbd {
                                  const std::string &global_image_id);
 
     // Consistency groups functions
-    int group_create(librados::IoCtx *ioctx, const std::string &oid);
     int group_dir_list(librados::IoCtx *ioctx, const std::string &oid,
                    const std::string &start, uint64_t max_return,
                    map<string, string> *groups);
@@ -424,6 +423,19 @@ namespace librbd {
                                cls::rbd::GroupSpec *group_spec);
     int image_get_group(librados::IoCtx *ioctx, const std::string &oid,
                        cls::rbd::GroupSpec *group_spec);
+    int group_snap_add(librados::IoCtx *ioctx, const std::string &oid,
+                      const cls::rbd::GroupSnapshot &snapshot);
+    int group_snap_update(librados::IoCtx *ioctx, const std::string &oid,
+                         const cls::rbd::GroupSnapshot &snapshot);
+    int group_snap_remove(librados::IoCtx *ioctx, const std::string &oid,
+                         const std::string &snap_id);
+    int group_snap_get_by_id(librados::IoCtx *ioctx, const std::string &oid,
+                            const std::string &snap_id,
+                            cls::rbd::GroupSnapshot *snapshot);
+    int group_snap_list(librados::IoCtx *ioctx, const std::string &oid,
+                       const cls::rbd::GroupSnapshot &start,
+                       uint64_t max_return,
+                       std::vector<cls::rbd::GroupSnapshot> *snapshots);
 
     // operations on rbd_trash object
     void trash_add(librados::ObjectWriteOperation *op,
index 8c1767c4edf8eabdc10ab8de3975d720fd4decb7..50af65ebbd490324666d57bae01638995a334248 100644 (file)
@@ -264,6 +264,11 @@ std::string GroupImageSpec::image_key() {
   }
 }
 
+void GroupImageSpec::generate_test_instances(std::list<GroupImageSpec*> &o) {
+  o.push_back(new GroupImageSpec("10152ae8944a", 0));
+  o.push_back(new GroupImageSpec("1018643c9869", 3));
+}
+
 void GroupImageStatus::encode(bufferlist &bl) const {
   ENCODE_START(1, 1, bl);
   ::encode(spec, bl);
@@ -294,6 +299,14 @@ void GroupImageStatus::dump(Formatter *f) const {
   f->dump_string("state", state_to_string());
 }
 
+void GroupImageStatus::generate_test_instances(std::list<GroupImageStatus*> &o) {
+  o.push_back(new GroupImageStatus(GroupImageSpec("10152ae8944a", 0), GROUP_IMAGE_LINK_STATE_ATTACHED));
+  o.push_back(new GroupImageStatus(GroupImageSpec("1018643c9869", 3), GROUP_IMAGE_LINK_STATE_ATTACHED));
+  o.push_back(new GroupImageStatus(GroupImageSpec("10152ae8944a", 0), GROUP_IMAGE_LINK_STATE_INCOMPLETE));
+  o.push_back(new GroupImageStatus(GroupImageSpec("1018643c9869", 3), GROUP_IMAGE_LINK_STATE_INCOMPLETE));
+}
+
+
 void GroupSpec::encode(bufferlist &bl) const {
   ENCODE_START(1, 1, bl);
   ::encode(pool_id, bl);
@@ -317,22 +330,27 @@ bool GroupSpec::is_valid() const {
   return (!group_id.empty()) && (pool_id != -1);
 }
 
+void GroupSpec::generate_test_instances(std::list<GroupSpec *> &o) {
+  o.push_back(new GroupSpec("10152ae8944a", 0));
+  o.push_back(new GroupSpec("1018643c9869", 3));
+}
+
 void GroupSnapshotNamespace::encode(bufferlist& bl) const {
   ::encode(group_pool, bl);
   ::encode(group_id, bl);
-  ::encode(snapshot_id, bl);
+  ::encode(group_snapshot_id, bl);
 }
 
 void GroupSnapshotNamespace::decode(bufferlist::iterator& it) {
   ::decode(group_pool, it);
   ::decode(group_id, it);
-  ::decode(snapshot_id, it);
+  ::decode(group_snapshot_id, it);
 }
 
 void GroupSnapshotNamespace::dump(Formatter *f) const {
   f->dump_int("group_pool", group_pool);
   f->dump_string("group_id", group_id);
-  f->dump_int("snapshot_id", snapshot_id);
+  f->dump_string("group_snapshot_id", group_snapshot_id);
 }
 
 class EncodeSnapshotNamespaceVisitor : public boost::static_visitor<void> {
@@ -388,8 +406,7 @@ public:
   }
 };
 
-
-SnapshotNamespaceType SnapshotNamespaceOnDisk::get_namespace_type() const {
+SnapshotNamespaceType get_namespace_type(const SnapshotNamespace& snapshot_namespace) {
   return static_cast<SnapshotNamespaceType>(boost::apply_visitor(GetTypeVisitor(),
                                                                 snapshot_namespace));
 }
@@ -426,8 +443,8 @@ void SnapshotNamespaceOnDisk::dump(Formatter *f) const {
 
 void SnapshotNamespaceOnDisk::generate_test_instances(std::list<SnapshotNamespaceOnDisk *> &o) {
   o.push_back(new SnapshotNamespaceOnDisk(UserSnapshotNamespace()));
-  o.push_back(new SnapshotNamespaceOnDisk(GroupSnapshotNamespace(0, "10152ae8944a", 1)));
-  o.push_back(new SnapshotNamespaceOnDisk(GroupSnapshotNamespace(5, "1018643c9869", 3)));
+  o.push_back(new SnapshotNamespaceOnDisk(GroupSnapshotNamespace(0, "10152ae8944a", "2118643c9732")));
+  o.push_back(new SnapshotNamespaceOnDisk(GroupSnapshotNamespace(5, "1018643c9869", "33352be8933c")));
 }
 
 std::ostream& operator<<(std::ostream& os, const UserSnapshotNamespace& ns) {
@@ -439,7 +456,7 @@ std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespace& ns) {
   os << "[group"
      << " group_pool=" << ns.group_pool
      << " group_id=" << ns.group_id
-     << " snapshot_id=" << ns.snapshot_id << "]";
+     << " group_snapshot_id=" << ns.group_snapshot_id << "]";
   return os;
 }
 
@@ -448,6 +465,61 @@ std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns) {
   return os;
 }
 
+void ImageSnapshotSpec::encode(bufferlist& bl) const {
+  ENCODE_START(1, 1, bl);
+  ::encode(pool, bl);
+  ::encode(image_id, bl);
+  ::encode(snap_id, bl);
+  ENCODE_FINISH(bl);
+}
+
+void ImageSnapshotSpec::decode(bufferlist::iterator& it) {
+  DECODE_START(1, it);
+  ::decode(pool, it);
+  ::decode(image_id, it);
+  ::decode(snap_id, it);
+  DECODE_FINISH(it);
+}
+
+void ImageSnapshotSpec::dump(Formatter *f) const {
+  f->dump_int("pool", pool);
+  f->dump_string("image_id", image_id);
+  f->dump_int("snap_id", snap_id);
+}
+
+void ImageSnapshotSpec::generate_test_instances(std::list<ImageSnapshotSpec *> &o) {
+  o.push_back(new ImageSnapshotSpec(0, "myimage", 2));
+  o.push_back(new ImageSnapshotSpec(1, "testimage", 7));
+}
+
+void GroupSnapshot::encode(bufferlist& bl) const {
+  ENCODE_START(1, 1, bl);
+  ::encode(id, bl);
+  ::encode(name, bl);
+  ::encode(state, bl);
+  ::encode(snaps, bl);
+  ENCODE_FINISH(bl);
+}
+
+void GroupSnapshot::decode(bufferlist::iterator& it) {
+  DECODE_START(1, it);
+  ::decode(id, it);
+  ::decode(name, it);
+  ::decode(state, it);
+  ::decode(snaps, it);
+  DECODE_FINISH(it);
+}
+
+void GroupSnapshot::dump(Formatter *f) const {
+  f->dump_string("id", id);
+  f->dump_string("name", name);
+  f->dump_int("state", state);
+}
+
+void GroupSnapshot::generate_test_instances(std::list<GroupSnapshot *> &o) {
+  o.push_back(new GroupSnapshot("10152ae8944a", "groupsnapshot1", GROUP_SNAPSHOT_STATE_PENDING));
+  o.push_back(new GroupSnapshot("1018643c9869", "groupsnapshot2", GROUP_SNAPSHOT_STATE_COMPLETE));
+}
 void TrashImageSpec::encode(bufferlist& bl) const {
   ENCODE_START(1, 1, bl);
   ::encode(source, bl);
index d50281d8bf4060599cd537c46a4eeff23f4c6941..51c653b81b95da3fd3a629e61db0a6b319a8236b 100644 (file)
@@ -173,6 +173,8 @@ struct GroupImageSpec {
   void decode(bufferlist::iterator &it);
   void dump(Formatter *f) const;
 
+  static void generate_test_instances(std::list<GroupImageSpec*> &o);
+
   std::string image_key();
 
 };
@@ -197,6 +199,8 @@ struct GroupImageStatus {
   void decode(bufferlist::iterator &it);
   void dump(Formatter *f) const;
 
+  static void generate_test_instances(std::list<GroupImageStatus*> &o);
+
   std::string state_to_string() const;
 };
 
@@ -214,17 +218,20 @@ struct GroupSpec {
   void decode(bufferlist::iterator &it);
   void dump(Formatter *f) const;
   bool is_valid() const;
+
+  static void generate_test_instances(std::list<GroupSpec *> &o);
 };
 
 WRITE_CLASS_ENCODER(GroupSpec);
 
 enum SnapshotNamespaceType {
   SNAPSHOT_NAMESPACE_TYPE_USER = 0,
-  SNAPSHOT_NAMESPACE_TYPE_GROUP = 1
+  SNAPSHOT_NAMESPACE_TYPE_GROUP = 1,
+  SNAPSHOT_NAMESPACE_TYPE_UNKNOWN = -1,
 };
 
 struct UserSnapshotNamespace {
-  static const uint32_t SNAPSHOT_NAMESPACE_TYPE = SNAPSHOT_NAMESPACE_TYPE_USER;
+  static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = SNAPSHOT_NAMESPACE_TYPE_USER;
 
   UserSnapshotNamespace() {}
 
@@ -246,19 +253,19 @@ struct UserSnapshotNamespace {
 std::ostream& operator<<(std::ostream& os, const UserSnapshotNamespace& ns);
 
 struct GroupSnapshotNamespace {
-  static const uint32_t SNAPSHOT_NAMESPACE_TYPE = SNAPSHOT_NAMESPACE_TYPE_GROUP;
+  static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = SNAPSHOT_NAMESPACE_TYPE_GROUP;
 
   GroupSnapshotNamespace() {}
 
   GroupSnapshotNamespace(int64_t _group_pool,
                         const string &_group_id,
-                         snapid_t _snapshot_id)
-    : group_id(_group_id), group_pool(_group_pool), snapshot_id(_snapshot_id) {
-  }
+                        const string &_group_snapshot_id) :group_pool(_group_pool),
+                                                     group_id(_group_id),
+                                                     group_snapshot_id(_group_snapshot_id) {}
 
-  std::string group_id;
   int64_t group_pool = 0;
-  snapid_t snapshot_id = CEPH_NOSNAP;
+  string group_id;
+  string group_snapshot_id;
 
   void encode(bufferlist& bl) const;
   void decode(bufferlist::iterator& it);
@@ -268,7 +275,7 @@ struct GroupSnapshotNamespace {
   inline bool operator==(const GroupSnapshotNamespace& gsn) const {
     return group_pool == gsn.group_pool &&
           group_id == gsn.group_id &&
-          snapshot_id == gsn.snapshot_id;
+          group_snapshot_id == gsn.group_snapshot_id;
   }
 
   inline bool operator<(const GroupSnapshotNamespace& gsn) const {
@@ -277,8 +284,9 @@ struct GroupSnapshotNamespace {
     } else if (group_id < gsn.group_id) {
       return true;
     } else {
-      return snapshot_id < gsn.snapshot_id;
+      return (group_snapshot_id < gsn.group_snapshot_id);
     }
+    return false;
   }
 
 };
@@ -286,7 +294,7 @@ struct GroupSnapshotNamespace {
 std::ostream& operator<<(std::ostream& os, const GroupSnapshotNamespace& ns);
 
 struct UnknownSnapshotNamespace {
-  static const uint32_t SNAPSHOT_NAMESPACE_TYPE = static_cast<uint32_t>(-1);
+  static const SnapshotNamespaceType SNAPSHOT_NAMESPACE_TYPE = SNAPSHOT_NAMESPACE_TYPE_UNKNOWN;
 
   UnknownSnapshotNamespace() {}
 
@@ -297,14 +305,16 @@ struct UnknownSnapshotNamespace {
     return true;
   }
 
-  inline bool operator<(const UnknownSnapshotNamespace& usn) const {
+  inline bool operator<(const UnknownSnapshotNamespace& gsn) const {
     return false;
   }
 };
 
 std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns);
 
-typedef boost::variant<UserSnapshotNamespace, GroupSnapshotNamespace, UnknownSnapshotNamespace> SnapshotNamespace;
+typedef boost::variant<UserSnapshotNamespace,
+                      GroupSnapshotNamespace,
+                      UnknownSnapshotNamespace> SnapshotNamespace;
 
 
 struct SnapshotNamespaceOnDisk {
@@ -314,8 +324,6 @@ struct SnapshotNamespaceOnDisk {
 
   SnapshotNamespace snapshot_namespace;
 
-  SnapshotNamespaceType get_namespace_type() const;
-
   void encode(bufferlist& bl) const;
   void decode(bufferlist::iterator& it);
   void dump(Formatter *f) const;
@@ -328,6 +336,67 @@ struct SnapshotNamespaceOnDisk {
 };
 WRITE_CLASS_ENCODER(SnapshotNamespaceOnDisk);
 
+SnapshotNamespaceType get_namespace_type(const SnapshotNamespace& snapshot_namespace);
+
+enum GroupSnapshotState {
+  GROUP_SNAPSHOT_STATE_PENDING = 0,
+  GROUP_SNAPSHOT_STATE_COMPLETE = 1,
+};
+
+inline void encode(const GroupSnapshotState &state, bufferlist& bl, uint64_t features=0)
+{
+  ::encode(static_cast<uint8_t>(state), bl);
+}
+
+inline void decode(GroupSnapshotState &state, bufferlist::iterator& it)
+{
+  uint8_t int_state;
+  ::decode(int_state, it);
+  state = static_cast<GroupSnapshotState>(int_state);
+}
+
+struct ImageSnapshotSpec {
+  int64_t pool;
+  string image_id;
+  snapid_t snap_id;
+
+  ImageSnapshotSpec() {}
+  ImageSnapshotSpec(int64_t _pool,
+                   string _image_id,
+                   snapid_t _snap_id) : pool(_pool),
+                                        image_id(_image_id),
+                                        snap_id(_snap_id) {}
+
+  void encode(bufferlist& bl) const;
+  void decode(bufferlist::iterator& it);
+
+  void dump(Formatter *f) const;
+
+  static void generate_test_instances(std::list<ImageSnapshotSpec *> &o);
+};
+WRITE_CLASS_ENCODER(ImageSnapshotSpec);
+
+struct GroupSnapshot {
+  std::string id;
+  std::string name;
+  GroupSnapshotState state;
+
+  GroupSnapshot() {}
+  GroupSnapshot(std::string _id,
+               std::string _name,
+               GroupSnapshotState _state) : id(_id),
+                                            name(_name),
+                                            state(_state) {}
+
+  vector<ImageSnapshotSpec> snaps;
+
+  void encode(bufferlist& bl) const;
+  void decode(bufferlist::iterator& it);
+  void dump(Formatter *f) const;
+
+  static void generate_test_instances(std::list<GroupSnapshot *> &o);
+};
+WRITE_CLASS_ENCODER(GroupSnapshot);
 enum TrashImageSource {
   TRASH_IMAGE_SOURCE_USER = 0,
   TRASH_IMAGE_SOURCE_MIRRORING = 1
index a13c183293116a15d02bcd097fa144288745d6bd..ead403c12adcee30953ebcbe5f0c184f5b1945d4 100644 (file)
@@ -1005,7 +1005,7 @@ TEST_F(TestClsRbd, snapshots)
 
 TEST_F(TestClsRbd, snapshots_namespaces)
 {
-  cls::rbd::SnapshotNamespace groupSnapNamespace = cls::rbd::GroupSnapshotNamespace(5, "1018643c9869", 3);
+  cls::rbd::SnapshotNamespace groupSnapNamespace = cls::rbd::GroupSnapshotNamespace(5, "1018643c9869", "3338524f9933");
   cls::rbd::SnapshotNamespace userSnapNamespace = cls::rbd::UserSnapshotNamespace();
   librados::IoCtx ioctx;
   ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
@@ -1952,18 +1952,6 @@ TEST_F(TestClsRbd, mirror_instances) {
   ASSERT_EQ(0U, instance_ids.size());
 }
 
-TEST_F(TestClsRbd, group_create) {
-  librados::IoCtx ioctx;
-  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
-
-  string group_id = "group_id";
-  ASSERT_EQ(0, group_create(&ioctx, group_id));
-
-  uint64_t psize;
-  time_t pmtime;
-  ASSERT_EQ(0, ioctx.stat(group_id, &psize, &pmtime));
-}
-
 TEST_F(TestClsRbd, group_dir_list) {
   librados::IoCtx ioctx;
   ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
@@ -2069,12 +2057,10 @@ void test_image_add(librados::IoCtx &ioctx, const string& group_id,
   ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
 
   auto it = keys.begin();
-  ASSERT_EQ(2U, keys.size());
+  ASSERT_EQ(1U, keys.size());
 
   string image_key = cls::rbd::GroupImageSpec(image_id, pool_id).image_key();
   ASSERT_EQ(image_key, *it);
-  ++it;
-  ASSERT_EQ("snap_seq", *it);
 }
 
 TEST_F(TestClsRbd, group_image_add) {
@@ -2082,7 +2068,7 @@ TEST_F(TestClsRbd, group_image_add) {
   ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
 
   string group_id = "group_id";
-  ASSERT_EQ(0, group_create(&ioctx, group_id));
+  ASSERT_EQ(0, ioctx.create(group_id, true));
 
   int64_t pool_id = ioctx.get_id();
   string image_id = "image_id";
@@ -2093,8 +2079,8 @@ TEST_F(TestClsRbd, group_image_remove) {
   librados::IoCtx ioctx;
   ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
 
-  string group_id = "group_id";
-  ASSERT_EQ(0, group_create(&ioctx, group_id));
+  string group_id = "group_id1";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
 
   int64_t pool_id = ioctx.get_id();
   string image_id = "image_id";
@@ -2104,16 +2090,15 @@ TEST_F(TestClsRbd, group_image_remove) {
   ASSERT_EQ(0, group_image_remove(&ioctx, group_id, spec));
   set<string> keys;
   ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
-  ASSERT_EQ(1U, keys.size());
-  ASSERT_EQ("snap_seq", *(keys.begin()));
+  ASSERT_EQ(0U, keys.size());
 }
 
 TEST_F(TestClsRbd, group_image_list) {
   librados::IoCtx ioctx;
   ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
 
-  string group_id = "group_id";
-  ASSERT_EQ(0, group_create(&ioctx, group_id));
+  string group_id = "group_id2";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
 
   int64_t pool_id = ioctx.get_id();
   string image_id = "imageid"; // Image id shouldn't contain underscores
@@ -2138,8 +2123,8 @@ TEST_F(TestClsRbd, group_image_clean) {
   librados::IoCtx ioctx;
   ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
 
-  string group_id = "group_id1";
-  ASSERT_EQ(0, group_create(&ioctx, group_id));
+  string group_id = "group_id3";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
 
   int64_t pool_id = ioctx.get_id();
   string image_id = "image_id";
@@ -2238,6 +2223,276 @@ TEST_F(TestClsRbd, image_get_group) {
   ASSERT_EQ(pool_id, spec.pool_id);
 }
 
+TEST_F(TestClsRbd, group_snap_add_empty_name) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_add_emtpy_name";
+
+  string group_id = "group_id_snap_add_empty_name";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id, "", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(-EINVAL, group_snap_add(&ioctx, group_id, snap));
+}
+
+TEST_F(TestClsRbd, group_snap_add_empty_id) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_add_empty_id";
+
+  string group_id = "group_id_snap_add_empty_id";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {"", "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(-EINVAL, group_snap_add(&ioctx, group_id, snap));
+}
+
+TEST_F(TestClsRbd, group_snap_add_duplicate_id) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_add_duplicate_id";
+
+  string group_id = "group_id_snap_add_duplicate_id";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id, "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  cls::rbd::GroupSnapshot snap1 = {snap_id, "snap_name1", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(-EEXIST, group_snap_add(&ioctx, group_id, snap1));
+}
+
+TEST_F(TestClsRbd, group_snap_add_duplicate_name) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_add_duplicate_name";
+
+  string group_id = "group_id_snap_add_duplicate_name";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id1 = "snap_id1";
+  cls::rbd::GroupSnapshot snap = {snap_id1, "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  string snap_id2 = "snap_id2";
+  cls::rbd::GroupSnapshot snap1 = {snap_id2, "snap_name", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(-EEXIST, group_snap_add(&ioctx, group_id, snap1));
+}
+
+TEST_F(TestClsRbd, group_snap_add) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_add";
+
+  string group_id = "group_id_snap_add";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id, "test_snapshot", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  set<string> keys;
+  ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+
+  auto it = keys.begin();
+  ASSERT_EQ(1U, keys.size());
+
+  string snap_key = group::snap_key(snap.id);
+  ASSERT_EQ(snap_key, *it);
+}
+
+TEST_F(TestClsRbd, group_snap_list) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_list";
+
+  string group_id = "group_id_snap_list";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id1 = "snap_id1";
+  cls::rbd::GroupSnapshot snap1 = {snap_id1, "test_snapshot1", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap1));
+
+  string snap_id2 = "snap_id2";
+  cls::rbd::GroupSnapshot snap2 = {snap_id2, "test_snapshot2", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap2));
+
+  std::vector<cls::rbd::GroupSnapshot> snapshots;
+  ASSERT_EQ(0, group_snap_list(&ioctx, group_id, cls::rbd::GroupSnapshot(), 10, &snapshots));
+  ASSERT_EQ(2U, snapshots.size());
+  ASSERT_EQ(snap_id1, snapshots[0].id);
+  ASSERT_EQ(snap_id2, snapshots[1].id);
+}
+
+static std::string hexify(int v) {
+  ostringstream oss;
+  oss << std::setw(8) << std::setfill('0') << std::hex << v;
+  //oss << v;
+  return oss.str();
+}
+
+TEST_F(TestClsRbd, group_snap_list_max_return) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_list_max_return";
+
+  string group_id = "group_id_snap_list_max_return";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  for (int i = 0; i < 15; ++i) {
+    string snap_id = "snap_id" + hexify(i);
+    cls::rbd::GroupSnapshot snap = {snap_id,
+                                   "test_snapshot" + hexify(i),
+                                   cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+    ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+  }
+
+  std::vector<cls::rbd::GroupSnapshot> snapshots;
+  ASSERT_EQ(0, group_snap_list(&ioctx, group_id, cls::rbd::GroupSnapshot(), 10, &snapshots));
+  ASSERT_EQ(10U, snapshots.size());
+
+  for (int i = 0; i < 10; ++i) {
+    string snap_id = "snap_id" + hexify(i);
+    ASSERT_EQ(snap_id, snapshots[i].id);
+  }
+
+  cls::rbd::GroupSnapshot last_snap = *snapshots.rbegin();
+
+  ASSERT_EQ(0, group_snap_list(&ioctx, group_id, last_snap, 10, &snapshots));
+  ASSERT_EQ(5U, snapshots.size());
+  for (int i = 10; i < 15; ++i) {
+    string snap_id = "snap_id" + hexify(i);
+    ASSERT_EQ(snap_id, snapshots[i - 10].id);
+  }
+}
+
+TEST_F(TestClsRbd, group_snap_update) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_update";
+
+  string group_id = "group_id_snap_update";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id, "test_snapshot", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE;
+
+  ASSERT_EQ(0, group_snap_update(&ioctx, group_id, snap));
+
+  std::vector<cls::rbd::GroupSnapshot> snapshots;
+  ASSERT_EQ(0, group_snap_list(&ioctx, group_id, cls::rbd::GroupSnapshot(), 10, &snapshots));
+  ASSERT_EQ(1U, snapshots.size());
+  ASSERT_EQ(snap_id, snapshots[0].id);
+  ASSERT_EQ(cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE, snapshots[0].state);
+}
+
+TEST_F(TestClsRbd, group_snap_update_empty_name) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_update_empty_name";
+
+  string group_id = "group_id_snap_update_empty_name";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id, "test_snapshot", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  snap.name = "";
+  snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE;
+
+  ASSERT_EQ(-EINVAL, group_snap_update(&ioctx, group_id, snap));
+}
+
+TEST_F(TestClsRbd, group_snap_update_empty_id) {
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_update_empty_id";
+
+  string group_id = "group_id_snap_update_empty_id";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id, "test_snapshot", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  snap.id = "";
+  snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE;
+
+  ASSERT_EQ(-EINVAL, group_snap_update(&ioctx, group_id, snap));
+}
+
+TEST_F(TestClsRbd, group_snap_remove) {
+  librados::IoCtx ioctx;
+
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_remove";
+
+  string group_id = "group_id_snap_remove";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id, "test_snapshot", cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  set<string> keys;
+  ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+
+  auto it = keys.begin();
+  ASSERT_EQ(1U, keys.size());
+
+  string snap_key = group::snap_key(snap.id);
+  ASSERT_EQ(snap_key, *it);
+
+  // Remove the snapshot
+
+  ASSERT_EQ(0, group_snap_remove(&ioctx, group_id, snap_id));
+
+  ASSERT_EQ(0, ioctx.omap_get_keys(group_id, "", 10, &keys));
+
+  ASSERT_EQ(0U, keys.size());
+}
+
+TEST_F(TestClsRbd, group_snap_get_by_id) {
+  librados::IoCtx ioctx;
+
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string image_id = "image_id_snap_get_by_id";
+
+  string group_id = "group_id_snap_get_by_id";
+  ASSERT_EQ(0, ioctx.create(group_id, true));
+
+  string snap_id = "snap_id";
+  cls::rbd::GroupSnapshot snap = {snap_id,
+                                  "test_snapshot",
+                                  cls::rbd::GROUP_SNAPSHOT_STATE_PENDING};
+  ASSERT_EQ(0, group_snap_add(&ioctx, group_id, snap));
+
+  cls::rbd::GroupSnapshot received_snap;
+  ASSERT_EQ(0, group_snap_get_by_id(&ioctx, group_id, snap_id, &received_snap));
+
+  ASSERT_EQ(snap.id, received_snap.id);
+  ASSERT_EQ(snap.name, received_snap.name);
+  ASSERT_EQ(snap.state, received_snap.state);
+}
 TEST_F(TestClsRbd, trash_methods)
 {
   librados::IoCtx ioctx;
@@ -2304,4 +2559,3 @@ TEST_F(TestClsRbd, trash_methods)
 
   ioctx.close();
 }
-
index df83d84f3616b6a4d1ba09e53d5dccf63d06cb72..5ea0ceb7503202734a502e965902adcd9dcb15a9 100644 (file)
@@ -418,6 +418,13 @@ TYPE(cls_rbd_snap)
 TYPE(cls::rbd::MirrorPeer)
 TYPE(cls::rbd::MirrorImage)
 TYPE(cls::rbd::MirrorImageMap)
+TYPE(cls::rbd::MirrorImageStatus)
+TYPE(cls::rbd::GroupImageSpec)
+TYPE(cls::rbd::GroupImageStatus)
+TYPE(cls::rbd::GroupSpec)
+TYPE(cls::rbd::SnapshotNamespaceOnDisk)
+TYPE(cls::rbd::ImageSnapshotSpec)
+TYPE(cls::rbd::GroupSnapshot)
 #endif
 
 #include "cls/lock/cls_lock_types.h"