From: Ricardo Dias Date: Fri, 24 Feb 2017 17:57:04 +0000 (+0000) Subject: cls_rbd: methods to manipulate the rbd_trash object X-Git-Tag: v12.0.2~106^2~11 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=c7003e054602e662d37cfdd8d0fa04285c787104;p=ceph.git cls_rbd: methods to manipulate the rbd_trash object Signed-off-by: Ricardo Dias --- diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index fd4e35d6489b..5dc53a08126e 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -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 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 data; + string last_read = trash::image_key(""); + int max_read = RBD_MAX_KEYS_READ; + + CLS_LOG(20, "trash_get_images"); + + do { + map 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::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; } diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc index 1a3841060fb3..8d7307d404d8 100644 --- a/src/cls/rbd/cls_rbd_client.cc +++ b/src/cls/rbd/cls_rbd_client.cc @@ -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 *entries) + { + assert(entries); + + try { + ::decode(*entries, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + + return 0; + } + + int trash_list(librados::IoCtx *ioctx, + map *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 diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h index 3512089b7e07..ebca318e9359 100644 --- a/src/cls/rbd/cls_rbd_client.h +++ b/src/cls/rbd/cls_rbd_client.h @@ -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 *entries); + int trash_list(librados::IoCtx *ioctx, + map *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 diff --git a/src/cls/rbd/cls_rbd_types.cc b/src/cls/rbd/cls_rbd_types.cc index ace0abc15ac4..81b12a0fc0b1 100644 --- a/src/cls/rbd/cls_rbd_types.cc +++ b/src/cls/rbd/cls_rbd_types.cc @@ -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 diff --git a/src/cls/rbd/cls_rbd_types.h b/src/cls/rbd/cls_rbd_types.h index 501454208301..7d8d1abcad2d 100644 --- a/src/cls/rbd/cls_rbd_types.h +++ b/src/cls/rbd/cls_rbd_types.h @@ -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(source), bl); +} + +inline void decode(TrashImageSource &source, bufferlist::iterator& it) +{ + uint8_t int_source; + ::decode(int_source, it); + source = static_cast(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 diff --git a/src/include/rbd_types.h b/src/include/rbd_types.h index cb00d3db48d0..939d4d348909 100644 --- a/src/include/rbd_types.h +++ b/src/include/rbd_types.h @@ -105,6 +105,8 @@ #define RBD_GROUP_DIRECTORY "rbd_group_directory" +#define RBD_TRASH "rbd_trash" + struct rbd_info { __le64 max_id; } __attribute__ ((packed)); diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index 472ea6ffcc04..6d0cc44ad551 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -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 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(); +} +