From efb05b5195d92e5fb0d49935dcb8b798c5e6bcb2 Mon Sep 17 00:00:00 2001 From: Victor Denisov Date: Tue, 27 Sep 2016 18:01:00 -0700 Subject: [PATCH] cls_rbd: Add group snapshot operations to cls_rbd Signed-off-by: Victor Denisov --- src/cls/rbd/cls_rbd.cc | 320 ++++++++++++++++++++++++++++++- src/cls/rbd/cls_rbd.h | 11 ++ src/cls/rbd/cls_rbd_client.cc | 85 +++++++- src/cls/rbd/cls_rbd_client.h | 14 +- src/cls/rbd/cls_rbd_types.cc | 88 ++++++++- src/cls/rbd/cls_rbd_types.h | 99 ++++++++-- src/test/cls_rbd/test_cls_rbd.cc | 306 ++++++++++++++++++++++++++--- src/test/encoding/types.h | 7 + 8 files changed, 862 insertions(+), 68 deletions(-) diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index 08f06985c29c9..94a5d8cf1fa8f 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -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 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 *group_snaps) +{ + int max_read = RBD_MAX_KEYS_READ; + std::map 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::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 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 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", diff --git a/src/cls/rbd/cls_rbd.h b/src/cls/rbd/cls_rbd.h index f08fc1b1f0a65..cac28a4a3d544 100644 --- a/src/cls/rbd/cls_rbd.h +++ b/src/cls/rbd/cls_rbd.h @@ -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 diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc index 90c25c6bcc295..b41428cec3c55 100644 --- a/src/cls/rbd/cls_rbd_client.cc +++ b/src/cls/rbd/cls_rbd_client.cc @@ -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 *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 *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 *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) { diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h index dd0b27d52bd8c..6a9bd04046a56 100644 --- a/src/cls/rbd/cls_rbd_client.h +++ b/src/cls/rbd/cls_rbd_client.h @@ -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 *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 *snapshots); // operations on rbd_trash object void trash_add(librados::ObjectWriteOperation *op, diff --git a/src/cls/rbd/cls_rbd_types.cc b/src/cls/rbd/cls_rbd_types.cc index 8c1767c4edf8e..50af65ebbd490 100644 --- a/src/cls/rbd/cls_rbd_types.cc +++ b/src/cls/rbd/cls_rbd_types.cc @@ -264,6 +264,11 @@ std::string GroupImageSpec::image_key() { } } +void GroupImageSpec::generate_test_instances(std::list &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 &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 &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 { @@ -388,8 +406,7 @@ public: } }; - -SnapshotNamespaceType SnapshotNamespaceOnDisk::get_namespace_type() const { +SnapshotNamespaceType get_namespace_type(const SnapshotNamespace& snapshot_namespace) { return static_cast(boost::apply_visitor(GetTypeVisitor(), snapshot_namespace)); } @@ -426,8 +443,8 @@ void SnapshotNamespaceOnDisk::dump(Formatter *f) const { void SnapshotNamespaceOnDisk::generate_test_instances(std::list &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 &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 &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); diff --git a/src/cls/rbd/cls_rbd_types.h b/src/cls/rbd/cls_rbd_types.h index d50281d8bf406..51c653b81b95d 100644 --- a/src/cls/rbd/cls_rbd_types.h +++ b/src/cls/rbd/cls_rbd_types.h @@ -173,6 +173,8 @@ struct GroupImageSpec { void decode(bufferlist::iterator &it); void dump(Formatter *f) const; + static void generate_test_instances(std::list &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 &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 &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(-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 SnapshotNamespace; +typedef boost::variant 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(state), bl); +} + +inline void decode(GroupSnapshotState &state, bufferlist::iterator& it) +{ + uint8_t int_state; + ::decode(int_state, it); + state = static_cast(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 &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 snaps; + + void encode(bufferlist& bl) const; + void decode(bufferlist::iterator& it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list &o); +}; +WRITE_CLASS_ENCODER(GroupSnapshot); enum TrashImageSource { TRASH_IMAGE_SOURCE_USER = 0, TRASH_IMAGE_SOURCE_MIRRORING = 1 diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index a13c183293116..ead403c12adce 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -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 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 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 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 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 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 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(); } - diff --git a/src/test/encoding/types.h b/src/test/encoding/types.h index df83d84f3616b..5ea0ceb750320 100644 --- a/src/test/encoding/types.h +++ b/src/test/encoding/types.h @@ -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" -- 2.39.5