]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
cls_rbd: methods to manipulate the rbd_trash object
authorRicardo Dias <rdias@suse.com>
Fri, 24 Feb 2017 17:57:04 +0000 (17:57 +0000)
committerRicardo Dias <rdias@suse.com>
Tue, 11 Apr 2017 11:09:41 +0000 (12:09 +0100)
Signed-off-by: Ricardo Dias <rdias@suse.com>
src/cls/rbd/cls_rbd.cc
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/include/rbd_types.h
src/test/cls_rbd/test_cls_rbd.cc

index fd4e35d6489b37db346152f0fd6bf2e9a1d59fac..5dc53a08126e5418cc1fec7361e71b9881803cf9 100644 (file)
@@ -4872,6 +4872,197 @@ int image_get_group(cls_method_context_t hctx,
   return 0;
 }
 
+namespace trash {
+
+static const std::string IMAGE_KEY_PREFIX("id_");
+
+std::string image_key(const std::string &image_id) {
+  return IMAGE_KEY_PREFIX + image_id;
+}
+
+std::string image_id_from_key(const std::string &key) {
+  return key.substr(IMAGE_KEY_PREFIX.size());
+}
+
+} // namespace trash
+
+/**
+ * Add an image entry to the rbd trash. Creates the trash object if
+ * needed, and stores the trash spec information of the deleted image.
+ *
+ * Input:
+ * @param id the id of the image
+ * @param trash_spec the spec info of the deleted image
+ *
+ * Output:
+ * @returns -EEXIST if the image id is already in the trash
+ * @returns 0 on success, negative error code on failure
+ */
+int trash_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+  int r = cls_cxx_create(hctx, false);
+  if (r < 0) {
+    CLS_ERR("could not create trash: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
+  string id;
+  cls::rbd::TrashImageSpec trash_spec;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(id, iter);
+    ::decode(trash_spec, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  if (!is_valid_id(id)) {
+    CLS_ERR("trash_add: invalid id '%s'", id.c_str());
+    return -EINVAL;
+  }
+
+  CLS_LOG(20, "trash_add id=%s", id.c_str());
+
+  string key = trash::image_key(id);
+  cls::rbd::TrashImageSpec tmp;
+  r = read_key(hctx, key, &tmp);
+  if (r < 0 && r != -ENOENT) {
+    CLS_ERR("could not read key %s entry from trash: %s", key.c_str(),
+            cpp_strerror(r).c_str());
+    return r;
+  } else if (r == 0) {
+    CLS_LOG(10, "id already exists");
+    return -EEXIST;
+  }
+
+  map<string, bufferlist> omap_vals;
+  ::encode(trash_spec, omap_vals[key]);
+  return cls_cxx_map_set_vals(hctx, &omap_vals);
+}
+
+/**
+ * Removes an image entry from the rbd trash object.
+ * image.
+ *
+ * Input:
+ * @param id the id of the image
+ *
+ * Output:
+ * @returns -ENOENT if the image id does not exist in the trash
+ * @returns 0 on success, negative error code on failure
+ */
+int trash_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+  string id;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(id, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  CLS_LOG(20, "trash_remove id=%s", id.c_str());
+
+  string key = trash::image_key(id);
+  bufferlist tmp;
+  int r = cls_cxx_map_get_val(hctx, key, &tmp);
+  if (r < 0) {
+    if (r != -ENOENT) {
+      CLS_ERR("error reading entry key %s: %s", key.c_str(), cpp_strerror(r).c_str());
+    }
+    return r;
+  }
+
+  r = cls_cxx_map_remove_key(hctx, key);
+  if (r < 0) {
+    CLS_ERR("error removing entry: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
+  return 0;
+}
+
+/**
+ * Returns the list of trash spec entries registered in the rbd_trash
+ * object.
+ *
+ * Output:
+ * @param data the map between image id and trash spec info
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+int trash_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+  map<string, cls::rbd::TrashImageSpec> data;
+  string last_read = trash::image_key("");
+  int max_read = RBD_MAX_KEYS_READ;
+
+  CLS_LOG(20, "trash_get_images");
+
+  do {
+    map<string, bufferlist> raw_data;
+    int r = cls_cxx_map_get_vals(hctx, last_read, trash::IMAGE_KEY_PREFIX,
+                                 max_read, &raw_data);
+    if (r < 0) {
+      CLS_ERR("failed to read the vals off of disk: %s", cpp_strerror(r).c_str());
+      return r;
+    }
+    if (raw_data.empty()) {
+      break;
+    }
+
+    map<string, bufferlist>::iterator it = raw_data.begin();
+    for (; it != raw_data.end(); ++it) {
+      ::decode(data[trash::image_id_from_key(it->first)], it->second);
+    }
+
+    if (r < max_read) {
+      break;
+    }
+
+    last_read = raw_data.rbegin()->first;
+  } while (max_read);
+
+  ::encode(data, *out);
+
+  return 0;
+}
+
+/**
+ * Returns the trash spec entry of an image registered in the rbd_trash
+ * object.
+ *
+ * Input:
+ * @param id the id of the image
+ *
+ * Output:
+ * @param out the trash spec entry
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+int trash_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+  string id;
+  try {
+    bufferlist::iterator iter = in->begin();
+    ::decode(id, iter);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  CLS_LOG(20, "trash_get_image id=%s", id.c_str());
+
+
+  string key = trash::image_key(id);
+  bufferlist bl;
+  int r = cls_cxx_map_get_val(hctx, key, out);
+  if (r != -ENOENT) {
+    CLS_ERR("error reading image from trash '%s': '%s'", id.c_str(),
+            cpp_strerror(r).c_str());
+  }
+  return r;
+}
+
 CLS_INIT(rbd)
 {
   CLS_LOG(20, "Loaded rbd class!");
@@ -4962,6 +5153,10 @@ 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_trash_add;
+  cls_method_handle_t h_trash_remove;
+  cls_method_handle_t h_trash_list;
+  cls_method_handle_t h_trash_get;
 
   cls_register("rbd", &h_class);
   cls_register_cxx_method(h_class, "create",
@@ -5227,5 +5422,20 @@ CLS_INIT(rbd)
   cls_register_cxx_method(h_class, "image_get_group",
                          CLS_METHOD_RD,
                          image_get_group, &h_image_get_group);
+
+  /* rbd_trash object methods */
+  cls_register_cxx_method(h_class, "trash_add",
+                          CLS_METHOD_RD | CLS_METHOD_WR,
+                          trash_add, &h_trash_add);
+  cls_register_cxx_method(h_class, "trash_remove",
+                          CLS_METHOD_RD | CLS_METHOD_WR,
+                          trash_remove, &h_trash_remove);
+  cls_register_cxx_method(h_class, "trash_list",
+                          CLS_METHOD_RD,
+                          trash_list, &h_trash_list);
+  cls_register_cxx_method(h_class, "trash_get",
+                          CLS_METHOD_RD,
+                          trash_get, &h_trash_get);
+
   return;
 }
index 1a3841060fb38ced4d4de4fc6702c7937775726c..8d7307d404d80b00e807361345eab3e0e0d6faa2 100644 (file)
@@ -1978,5 +1978,114 @@ namespace librbd {
       return image_get_group_finish(&iter, group_spec);
     }
 
+    // rbd_trash functions
+    void trash_add(librados::ObjectWriteOperation *op,
+                  const std::string &id,
+                   const cls::rbd::TrashImageSpec &trash_spec)
+    {
+      bufferlist bl;
+      ::encode(id, bl);
+      ::encode(trash_spec, bl);
+      op->exec("rbd", "trash_add", bl);
+    }
+
+    int trash_add(librados::IoCtx *ioctx, const std::string &id,
+                  const cls::rbd::TrashImageSpec &trash_spec)
+    {
+      librados::ObjectWriteOperation op;
+      trash_add(&op, id, trash_spec);
+
+      return ioctx->operate(RBD_TRASH, &op);
+    }
+
+    void trash_remove(librados::ObjectWriteOperation *op,
+                     const std::string &id)
+    {
+      bufferlist bl;
+      ::encode(id, bl);
+      op->exec("rbd", "trash_remove", bl);
+    }
+
+    int trash_remove(librados::IoCtx *ioctx, const std::string &id)
+    {
+      librados::ObjectWriteOperation op;
+      trash_remove(&op, id);
+
+      return ioctx->operate(RBD_TRASH, &op);
+    }
+
+    void trash_list_start(librados::ObjectReadOperation *op)
+    {
+      bufferlist bl;
+      op->exec("rbd", "trash_list", bl);
+    }
+
+    int trash_list_finish(bufferlist::iterator *it,
+                          map<string, cls::rbd::TrashImageSpec> *entries)
+    {
+      assert(entries);
+
+      try {
+       ::decode(*entries, *it);
+      } catch (const buffer::error &err) {
+       return -EBADMSG;
+      }
+
+      return 0;
+    }
+
+    int trash_list(librados::IoCtx *ioctx,
+                   map<string, cls::rbd::TrashImageSpec> *entries)
+    {
+      librados::ObjectReadOperation op;
+      trash_list_start(&op);
+
+      bufferlist out_bl;
+      int r = ioctx->operate(RBD_TRASH, &op, &out_bl);
+      if (r < 0) {
+       return r;
+      }
+
+      bufferlist::iterator iter = out_bl.begin();
+      return trash_list_finish(&iter, entries);
+    }
+
+    void trash_get_start(librados::ObjectReadOperation *op,
+                        const std::string &id)
+    {
+      bufferlist bl;
+      ::encode(id, bl);
+      op->exec("rbd", "trash_get", bl);
+    }
+
+    int trash_get_finish(bufferlist::iterator *it,
+                          cls::rbd::TrashImageSpec *trash_spec) {
+      assert(trash_spec);
+      try {
+        ::decode(*trash_spec, *it);
+      } catch (const buffer::error &err) {
+        return -EBADMSG;
+      }
+
+      return 0;
+    }
+
+
+    int trash_get(librados::IoCtx *ioctx, const std::string &id,
+                  cls::rbd::TrashImageSpec *trash_spec)
+    {
+      librados::ObjectReadOperation op;
+      trash_get_start(&op, id);
+
+      bufferlist out_bl;
+      int r = ioctx->operate(RBD_TRASH, &op, &out_bl);
+      if (r < 0) {
+        return r;
+      }
+
+      bufferlist::iterator it = out_bl.begin();
+      return trash_get_finish(&it, trash_spec);
+    }
+
   } // namespace cls_client
 } // namespace librbd
index 3512089b7e070c25bd8bd6aa47b10a085cb90715..ebca318e93594bb09018b5b0a2c291d871aa4300 100644 (file)
@@ -403,6 +403,27 @@ namespace librbd {
     int image_get_group(librados::IoCtx *ioctx, const std::string &oid,
                        cls::rbd::GroupSpec *group_spec);
 
+    // operations on rbd_trash object
+    void trash_add(librados::ObjectWriteOperation *op,
+                   const std::string &id,
+                   const cls::rbd::TrashImageSpec &trash_spec);
+    int trash_add(librados::IoCtx *ioctx, const std::string &id,
+                  const cls::rbd::TrashImageSpec &trash_spec);
+    void trash_remove(librados::ObjectWriteOperation *op,
+                      const std::string &id);
+    int trash_remove(librados::IoCtx *ioctx, const std::string &id);
+    void trash_list_start(librados::ObjectReadOperation *op);
+    int trash_list_finish(bufferlist::iterator *it,
+                          map<string, cls::rbd::TrashImageSpec> *entries);
+    int trash_list(librados::IoCtx *ioctx,
+                   map<string, cls::rbd::TrashImageSpec> *entries);
+    void trash_get_start(librados::ObjectReadOperation *op,
+                         const std::string &id);
+    int trash_get_finish(bufferlist::iterator *it,
+                         cls::rbd::TrashImageSpec *trash_spec);
+    int trash_get(librados::IoCtx *ioctx, const std::string &id,
+                  cls::rbd::TrashImageSpec *trash_spec);
+
   } // namespace cls_client
 } // namespace librbd
 #endif // CEPH_LIBRBD_CLS_RBD_CLIENT_H
index ace0abc15ac4fab0b5decb01ec5ded7e7db2c8cd..81b12a0fc0b1d290bdf0891ce081f2a494526a41 100644 (file)
@@ -448,5 +448,36 @@ std::ostream& operator<<(std::ostream& os, const UnknownSnapshotNamespace& ns) {
   return os;
 }
 
+void TrashImageSpec::encode(bufferlist& bl) const {
+  ENCODE_START(1, 1, bl);
+  ::encode(source, bl);
+  ::encode(name, bl);
+  ::encode(deletion_time, bl);
+  ::encode(deferment_end_time, bl);
+  ENCODE_FINISH(bl);
+}
+
+void TrashImageSpec::decode(bufferlist::iterator &it) {
+  DECODE_START(1, it);
+  ::decode(source, it);
+  ::decode(name, it);
+  ::decode(deletion_time, it);
+  ::decode(deferment_end_time, it);
+  DECODE_FINISH(it);
+}
+
+void TrashImageSpec::dump(Formatter *f) const {
+  switch(source) {
+    case TRASH_IMAGE_SOURCE_USER:
+      f->dump_string("source", "user");
+      break;
+    case TRASH_IMAGE_SOURCE_MIRRORING:
+      f->dump_string("source", "rbd_mirror");
+  }
+  f->dump_string("name", name);
+  f->dump_unsigned("deletion_time", deletion_time);
+  f->dump_unsigned("deferment_end_time", deferment_end_time);
+}
+
 } // namespace rbd
 } // namespace cls
index 50145420830130046e0d4aeec9e86c1e8811d94b..7d8d1abcad2dd5c6d8704547d7647e12c9408db6 100644 (file)
@@ -328,6 +328,42 @@ struct SnapshotNamespaceOnDisk {
 };
 WRITE_CLASS_ENCODER(SnapshotNamespaceOnDisk);
 
+enum TrashImageSource {
+  TRASH_IMAGE_SOURCE_USER = 0,
+  TRASH_IMAGE_SOURCE_MIRRORING = 1
+};
+
+inline void encode(const TrashImageSource &source, bufferlist& bl,
+                  uint64_t features=0)
+{
+  ::encode(static_cast<uint8_t>(source), bl);
+}
+
+inline void decode(TrashImageSource &source, bufferlist::iterator& it)
+{
+  uint8_t int_source;
+  ::decode(int_source, it);
+  source = static_cast<TrashImageSource>(int_source);
+}
+
+struct TrashImageSpec {
+  TrashImageSource source = TRASH_IMAGE_SOURCE_USER;
+  std::string name;
+  utime_t deletion_time; // time of deletion
+  utime_t deferment_end_time;
+
+  TrashImageSpec() {}
+  TrashImageSpec(TrashImageSource source, const std::string &name,
+                   utime_t deletion_time, utime_t deferment_end_time) :
+    source(source), name(name), deletion_time(deletion_time),
+    deferment_end_time(deferment_end_time) {}
+
+  void encode(bufferlist &bl) const;
+  void decode(bufferlist::iterator& it);
+  void dump(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(TrashImageSpec);
+
 } // namespace rbd
 } // namespace cls
 
index cb00d3db48d07d114356abc3adbe3cbe4d2a5cf8..939d4d34890985b6fce4b86322c4ba11721132e4 100644 (file)
 
 #define RBD_GROUP_DIRECTORY "rbd_group_directory"
 
+#define RBD_TRASH "rbd_trash"
+
 struct rbd_info {
        __le64 max_id;
 } __attribute__ ((packed));
index 472ea6ffcc04f6b9b545b2d6ed3bf7e446ae01ca..6d0cc44ad551e88ce48ff9cc5561cbcd4dee659b 100644 (file)
@@ -4,6 +4,7 @@
 #include "common/ceph_context.h"
 #include "common/config.h"
 #include "common/snap_types.h"
+#include "common/Clock.h"
 #include "include/encoding.h"
 #include "include/types.h"
 #include "include/rados/librados.h"
@@ -2222,3 +2223,70 @@ TEST_F(TestClsRbd, image_get_group) {
   ASSERT_EQ(group_id, spec.group_id);
   ASSERT_EQ(pool_id, spec.pool_id);
 }
+
+TEST_F(TestClsRbd, trash_methods)
+{
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string id = "123456789";
+  string id2 = "123456780";
+
+  std::map<string, cls::rbd::TrashImageSpec> entries;
+  ASSERT_EQ(-ENOENT, trash_list(&ioctx, &entries));
+
+  utime_t now1 = ceph_clock_now();
+  utime_t now1_delay = now1;
+  now1_delay += 380;
+  cls::rbd::TrashImageSpec trash_spec(cls::rbd::TRASH_IMAGE_SOURCE_USER, "name",
+                                      now1, now1_delay);
+  ASSERT_EQ(0, trash_add(&ioctx, id, trash_spec));
+
+  utime_t now2 = ceph_clock_now();
+  utime_t now2_delay = now2;
+  now2_delay += 480;
+  cls::rbd::TrashImageSpec trash_spec2(cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING,
+                                       "name2", now2, now2_delay);
+  ASSERT_EQ(-EEXIST, trash_add(&ioctx, id, trash_spec2));
+
+  ASSERT_EQ(0, trash_remove(&ioctx, id));
+  ASSERT_EQ(-ENOENT, trash_remove(&ioctx, id));
+
+  ASSERT_EQ(0, trash_list(&ioctx, &entries));
+  ASSERT_TRUE(entries.empty());
+
+  ASSERT_EQ(0, trash_add(&ioctx, id, trash_spec2));
+  ASSERT_EQ(0, trash_add(&ioctx, id2, trash_spec));
+
+  ASSERT_EQ(0, trash_list(&ioctx, &entries));
+
+  for (auto& entry : entries) {
+    if (entry.first == id) {
+      ASSERT_EQ(entry.second.source, cls::rbd::TRASH_IMAGE_SOURCE_MIRRORING);
+      ASSERT_EQ(entry.second.name, "name2");
+      ASSERT_EQ(entry.second.deletion_time, now2);
+      ASSERT_EQ(entry.second.deferment_end_time, now2_delay);
+    } else if (entry.first == id2) {
+      ASSERT_EQ(entry.second.source, cls::rbd::TRASH_IMAGE_SOURCE_USER);
+      ASSERT_EQ(entry.second.name, "name");
+      ASSERT_EQ(entry.second.deletion_time, now1);
+      ASSERT_EQ(entry.second.deferment_end_time, now1_delay);
+    }
+  }
+
+  cls::rbd::TrashImageSpec spec_res1;
+  ASSERT_EQ(0, trash_get(&ioctx, id, &spec_res1));
+  cls::rbd::TrashImageSpec spec_res2;
+  ASSERT_EQ(0, trash_get(&ioctx, id2, &spec_res2));
+
+  ASSERT_EQ(spec_res1.name, "name2");
+  ASSERT_EQ(spec_res1.deletion_time, now2);
+  ASSERT_EQ(spec_res1.deferment_end_time, now2_delay);
+
+  ASSERT_EQ(spec_res2.name, "name");
+  ASSERT_EQ(spec_res2.deletion_time, now1);
+  ASSERT_EQ(spec_res2.deferment_end_time, now1_delay);
+
+  ioctx.close();
+}
+