From b0b1c44826cf23a444aac0fb5a74dbd3d4afb290 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Thu, 18 Jan 2018 20:10:58 -0500 Subject: [PATCH] cls/rbd: new cls methods for handling clone v2 Signed-off-by: Jason Dillaman --- src/cls/rbd/cls_rbd.cc | 332 +++++++++++++++++++++++++++---- src/cls/rbd/cls_rbd_client.cc | 87 ++++++++ src/cls/rbd/cls_rbd_client.h | 18 ++ src/cls/rbd/cls_rbd_types.cc | 24 +++ src/cls/rbd/cls_rbd_types.h | 32 ++- src/test/cls_rbd/test_cls_rbd.cc | 103 ++++++++++ src/test/encoding/types.h | 1 + 7 files changed, 555 insertions(+), 42 deletions(-) diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index 301a8540bde04..97ce99a9fee53 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -60,12 +60,11 @@ CLS_NAME(rbd) #define RBD_MAX_KEYS_READ 64 #define RBD_SNAP_KEY_PREFIX "snapshot_" +#define RBD_SNAP_CHILDREN_KEY_PREFIX "snap_children_" #define RBD_DIR_ID_KEY_PREFIX "id_" #define RBD_DIR_NAME_KEY_PREFIX "name_" #define RBD_METADATA_KEY_PREFIX "metadata_" -#define GROUP_SNAP_SEQ "snap_seq" - static int snap_read_header(cls_method_context_t hctx, bufferlist& bl) { unsigned snap_count = 0; @@ -140,6 +139,19 @@ static int read_key(cls_method_context_t hctx, const string &key, T *out) return 0; } +template +static int write_key(cls_method_context_t hctx, const string &key, const T &t) { + bufferlist bl; + encode(t, bl); + + int r = cls_cxx_map_set_val(hctx, key, &bl); + if (r < 0) { + CLS_ERR("failed to set omap key: %s", key.c_str()); + return r; + } + return 0; +} + static int remove_key(cls_method_context_t hctx, const string &key) { int r = cls_cxx_map_remove_key(hctx, key); if (r < 0 && r != -ENOENT) { @@ -162,6 +174,14 @@ static bool is_valid_id(const string &id) { namespace image { +std::string snap_children_key_from_snap_id(snapid_t snap_id) +{ + ostringstream oss; + oss << RBD_SNAP_CHILDREN_KEY_PREFIX + << std::setw(16) << std::setfill('0') << std::hex << snap_id; + return oss.str(); +} + int set_op_features(cls_method_context_t hctx, uint64_t op_features, uint64_t mask) { uint64_t orig_features; @@ -1229,51 +1249,30 @@ int remove_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out) return r; } - // remove the parent from all snapshots - if ((features & RBD_FEATURE_DEEP_FLATTEN) != 0) { - int max_read = RBD_MAX_KEYS_READ; - vector snap_ids; - string last_read = RBD_SNAP_KEY_PREFIX; - bool more; - - do { - set keys; - r = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys, &more); - if (r < 0) { - return r; - } - - for (std::set::const_iterator it = keys.begin(); - it != keys.end(); ++it) { - if ((*it).find(RBD_SNAP_KEY_PREFIX) != 0) { - break; - } - - uint64_t snap_id = snap_id_from_key(*it); - cls_rbd_snap snap_meta; - r = read_key(hctx, *it, &snap_meta); - if (r < 0) { - CLS_ERR("Could not read snapshot: snap_id=%" PRIu64 ": %s", - snap_id, cpp_strerror(r).c_str()); - return r; - } - - snap_meta.parent = cls_rbd_parent(); + auto flatten_lambda = [hctx, features](const cls_rbd_snap& snap_meta) { + if (snap_meta.parent.pool != -1) { + if ((features & RBD_FEATURE_DEEP_FLATTEN) != 0ULL) { + // remove parent reference from snapshot + cls_rbd_snap snap_meta_copy = snap_meta; + snap_meta_copy.parent = cls_rbd_parent(); - bufferlist bl; - encode(snap_meta, bl); - r = cls_cxx_map_set_val(hctx, *it, &bl); + std::string snap_key; + key_from_snap_id(snap_meta_copy.id, &snap_key); + int r = write_key(hctx, snap_key, snap_meta_copy); if (r < 0) { - CLS_ERR("Could not update snapshot: snap_id=%" PRIu64 ": %s", - snap_id, cpp_strerror(r).c_str()); return r; } + } else { + return -EEXIST; } + } + return 0; + }; - if (!keys.empty()) { - last_read = *(keys.rbegin()); - } - } while (more); + r = image::snapshot_iterate(hctx, flatten_lambda); + bool has_child_snaps = (r == -EEXIST); + if (r < 0 && r != -EEXIST) { + return r; } cls_rbd_parent parent; @@ -1286,6 +1285,15 @@ int remove_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out) CLS_ERR("error removing parent: %s", cpp_strerror(r).c_str()); return r; } + + if (!has_child_snaps) { + // disable clone child op feature if no longer associated + r = image::set_op_features(hctx, 0, RBD_OPERATION_FEATURE_CLONE_CHILD); + if (r < 0) { + return r; + } + } + return 0; } @@ -1950,12 +1958,33 @@ int snapshot_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out) if (snap.protection_status != RBD_PROTECTION_STATUS_UNPROTECTED) return -EBUSY; + auto remove_lambda = [snap_id](const cls_rbd_snap& snap_meta) { + if (snap_meta.id != snap_id && snap_meta.parent.pool != -1) { + return -EEXIST; + } + return 0; + }; + + r = image::snapshot_iterate(hctx, remove_lambda); + bool has_child_snaps = (r == -EEXIST); + if (r < 0 && r != -EEXIST) { + return r; + } + r = cls_cxx_map_remove_key(hctx, snapshot_key); if (r < 0) { CLS_ERR("error writing snapshot metadata: %s", cpp_strerror(r).c_str()); return r; } + if (!has_child_snaps) { + // disable clone child op feature if no longer associated + r = image::set_op_features(hctx, 0, RBD_OPERATION_FEATURE_CLONE_CHILD); + if (r < 0) { + return r; + } + } + return 0; } @@ -3008,6 +3037,215 @@ int snapshot_set_limit(cls_method_context_t hctx, bufferlist *in, } +/** + * Input: + * @param snap id (uint64_t) parent snapshot id + * @param child spec (cls::rbd::ChildImageSpec) child image + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int child_attach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + cls::rbd::ChildImageSpec child_image; + try { + bufferlist::iterator it = in->begin(); + decode(snap_id, it); + decode(child_image, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "child_attach snap_id=%" PRIu64 ", child_pool_id=%" PRIi64 ", " + "child_image_id=%s", snap_id, child_image.pool_id, + child_image.image_id.c_str()); + + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + if (cls::rbd::get_snap_namespace_type(snap.snapshot_namespace) == + cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) { + // cannot attach to a deleted snapshot + return -ENOENT; + } + + auto children_key = image::snap_children_key_from_snap_id(snap_id); + cls::rbd::ChildImageSpecs child_images; + r = read_key(hctx, children_key, &child_images); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + auto it = child_images.insert(child_image); + if (!it.second) { + // child already attached to the snapshot + return -EEXIST; + } + + r = write_key(hctx, children_key, child_images); + if (r < 0) { + CLS_ERR("error writing snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + ++snap.child_count; + r = write_key(hctx, snapshot_key, snap); + if (r < 0) { + CLS_ERR("error writing snapshot: %s", cpp_strerror(r).c_str()); + return r; + } + + r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_CLONE_PARENT, + RBD_OPERATION_FEATURE_CLONE_PARENT); + if (r < 0) { + return r; + } + + return 0; +} + +/** + * Input: + * @param snap id (uint64_t) parent snapshot id + * @param child spec (cls::rbd::ChildImageSpec) child image + * + * Output: + * @returns 0 on success, negative error code on failure + */ +int child_detach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + cls::rbd::ChildImageSpec child_image; + try { + bufferlist::iterator it = in->begin(); + decode(snap_id, it); + decode(child_image, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "child_detach snap_id=%" PRIu64 ", child_pool_id=%" PRIi64 ", " + "child_image_id=%s", snap_id, child_image.pool_id, + child_image.image_id.c_str()); + + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + auto children_key = image::snap_children_key_from_snap_id(snap_id); + cls::rbd::ChildImageSpecs child_images; + r = read_key(hctx, children_key, &child_images); + if (r < 0 && r != -ENOENT) { + CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + if (snap.child_count != child_images.size()) { + // children and reference count don't match + CLS_ERR("children reference count mismatch: %" PRIu64, snap_id); + return -EINVAL; + } + + if (child_images.erase(child_image) == 0) { + // child not attached to the snapshot + return -ENOENT; + } + + if (child_images.empty()) { + r = remove_key(hctx, children_key); + } else { + r = write_key(hctx, children_key, child_images); + if (r < 0) { + CLS_ERR("error writing snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + } + + --snap.child_count; + r = write_key(hctx, snapshot_key, snap); + if (r < 0) { + CLS_ERR("error writing snapshot: %s", cpp_strerror(r).c_str()); + return r; + } + + if (snap.child_count == 0) { + auto clone_in_use_lambda = [snap_id](const cls_rbd_snap& snap_meta) { + if (snap_meta.id != snap_id && snap_meta.child_count > 0) { + return -EEXIST; + } + return 0; + }; + + r = image::snapshot_iterate(hctx, clone_in_use_lambda); + if (r < 0 && r != -EEXIST) { + return r; + } + + if (r != -EEXIST) { + // remove the clone_v2 op feature if not in-use by any other snapshots + r = image::set_op_features(hctx, 0, RBD_OPERATION_FEATURE_CLONE_PARENT); + if (r < 0) { + return r; + } + } + } + + return 0; +} + +/** + * Input: + * @param snap id (uint64_t) parent snapshot id + * + * Output: + * @param (cls::rbd::ChildImageSpecs) child images + * @returns 0 on success, negative error code on failure + */ +int children_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + uint64_t snap_id; + try { + bufferlist::iterator it = in->begin(); + decode(snap_id, it); + } catch (const buffer::error &err) { + return -EINVAL; + } + + CLS_LOG(20, "child_detach snap_id=%" PRIu64, snap_id); + + cls_rbd_snap snap; + std::string snapshot_key; + key_from_snap_id(snap_id, &snapshot_key); + int r = read_key(hctx, snapshot_key, &snap); + if (r < 0) { + return r; + } + + auto children_key = image::snap_children_key_from_snap_id(snap_id); + cls::rbd::ChildImageSpecs child_images; + r = read_key(hctx, children_key, &child_images); + if (r == -ENOENT) { + return r; + } else if (r < 0) { + CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str()); + return r; + } + + encode(child_images, *out); + return 0; +} + /****************************** Old format *******************************/ int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out) @@ -5737,6 +5975,9 @@ CLS_INIT(rbd) cls_method_handle_t h_metadata_get; cls_method_handle_t h_snapshot_get_limit; cls_method_handle_t h_snapshot_set_limit; + cls_method_handle_t h_child_attach; + cls_method_handle_t h_child_detach; + cls_method_handle_t h_children_list; cls_method_handle_t h_old_snapshots_list; cls_method_handle_t h_old_snapshot_add; cls_method_handle_t h_old_snapshot_remove; @@ -5886,6 +6127,15 @@ CLS_INIT(rbd) cls_register_cxx_method(h_class, "snapshot_set_limit", CLS_METHOD_RD | CLS_METHOD_WR, snapshot_set_limit, &h_snapshot_set_limit); + cls_register_cxx_method(h_class, "child_attach", + CLS_METHOD_RD | CLS_METHOD_WR, + child_attach, &h_child_attach); + cls_register_cxx_method(h_class, "child_detach", + CLS_METHOD_RD | CLS_METHOD_WR, + child_detach, &h_child_detach); + cls_register_cxx_method(h_class, "children_list", + CLS_METHOD_RD, + children_list, &h_children_list); /* methods for the rbd_children object */ cls_register_cxx_method(h_class, "add_child", diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc index 9c7f613c60eba..fa19a5b662639 100644 --- a/src/cls/rbd/cls_rbd_client.cc +++ b/src/cls/rbd/cls_rbd_client.cc @@ -1442,6 +1442,93 @@ namespace librbd { return 0; } + void child_attach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) + { + bufferlist bl; + encode(snap_id, bl); + encode(child_image, bl); + op->exec("rbd", "child_attach", bl); + } + + int child_attach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) + { + librados::ObjectWriteOperation op; + child_attach(&op, snap_id, child_image); + + int r = ioctx->operate(oid, &op); + if (r < 0) { + return r; + } + return 0; + } + + void child_detach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) + { + bufferlist bl; + encode(snap_id, bl); + encode(child_image, bl); + op->exec("rbd", "child_detach", bl); + } + + int child_detach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image) + { + librados::ObjectWriteOperation op; + child_detach(&op, snap_id, child_image); + + int r = ioctx->operate(oid, &op); + if (r < 0) { + return r; + } + return 0; + } + + void children_list_start(librados::ObjectReadOperation *op, + snapid_t snap_id) + { + bufferlist bl; + encode(snap_id, bl); + op->exec("rbd", "children_list", bl); + } + + int children_list_finish(bufferlist::iterator *it, + cls::rbd::ChildImageSpecs *child_images) + { + child_images->clear(); + try { + decode(*child_images, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; + } + + int children_list(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + cls::rbd::ChildImageSpecs *child_images) + { + librados::ObjectReadOperation op; + children_list_start(&op, snap_id); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + bufferlist::iterator it = out_bl.begin(); + r = children_list_finish(&it, child_images); + if (r < 0) { + return r; + } + return 0; + } + void mirror_uuid_get_start(librados::ObjectReadOperation *op) { bufferlist bl; op->exec("rbd", "mirror_uuid_get", bl); diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h index a9b08e923b3f8..6225f055e61ce 100644 --- a/src/cls/rbd/cls_rbd_client.h +++ b/src/cls/rbd/cls_rbd_client.h @@ -231,6 +231,24 @@ namespace librbd { int metadata_get(librados::IoCtx *ioctx, const std::string &oid, const std::string &key, string *v); + void child_attach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); + int child_attach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); + void child_detach(librados::ObjectWriteOperation *op, snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); + int child_detach(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + const cls::rbd::ChildImageSpec& child_image); + void children_list_start(librados::ObjectReadOperation *op, + snapid_t snap_id); + int children_list_finish(bufferlist::iterator *it, + cls::rbd::ChildImageSpecs *child_images); + int children_list(librados::IoCtx *ioctx, const std::string &oid, + snapid_t snap_id, + cls::rbd::ChildImageSpecs *child_images); + // operations on rbd_id objects void get_id_start(librados::ObjectReadOperation *op); int get_id_finish(bufferlist::iterator *it, std::string *id); diff --git a/src/cls/rbd/cls_rbd_types.cc b/src/cls/rbd/cls_rbd_types.cc index c6f5196a06ea1..d709ba80f4ec8 100644 --- a/src/cls/rbd/cls_rbd_types.cc +++ b/src/cls/rbd/cls_rbd_types.cc @@ -212,6 +212,30 @@ std::ostream& operator<<(std::ostream& os, const MirrorImageStatus& status) { return os; } +void ChildImageSpec::encode(bufferlist &bl) const { + ENCODE_START(1, 1, bl); + encode(pool_id, bl); + encode(image_id, bl); + ENCODE_FINISH(bl); +} + +void ChildImageSpec::decode(bufferlist::iterator &it) { + DECODE_START(1, it); + decode(pool_id, it); + decode(image_id, it); + DECODE_FINISH(it); +} + +void ChildImageSpec::dump(Formatter *f) const { + f->dump_int("pool_id", pool_id); + f->dump_string("image_id", image_id); +} + +void ChildImageSpec::generate_test_instances(std::list &o) { + o.push_back(new ChildImageSpec()); + o.push_back(new ChildImageSpec(123, "abc")); +} + void GroupImageSpec::encode(bufferlist &bl) const { ENCODE_START(1, 1, bl); encode(image_id, bl); diff --git a/src/cls/rbd/cls_rbd_types.h b/src/cls/rbd/cls_rbd_types.h index 2d2a2a138db6d..f34b33773c2c7 100644 --- a/src/cls/rbd/cls_rbd_types.h +++ b/src/cls/rbd/cls_rbd_types.h @@ -12,6 +12,7 @@ #include "include/utime.h" #include #include +#include #define RBD_GROUP_REF "rbd_group_ref" @@ -162,6 +163,36 @@ std::ostream& operator<<(std::ostream& os, const MirrorImageStatusState& state); WRITE_CLASS_ENCODER(MirrorImageStatus); +struct ChildImageSpec { + int64_t pool_id = -1; + std::string image_id; + + ChildImageSpec() {} + ChildImageSpec(int64_t pool_id, const std::string& image_id) + : pool_id(pool_id), image_id(image_id) { + } + + void encode(bufferlist &bl) const; + void decode(bufferlist::iterator &it); + void dump(Formatter *f) const; + + static void generate_test_instances(std::list &o); + + inline bool operator==(const ChildImageSpec& rhs) const { + return (pool_id == rhs.pool_id && + image_id == rhs.image_id); + } + inline bool operator<(const ChildImageSpec& rhs) const { + if (pool_id != rhs.pool_id) { + return pool_id < rhs.pool_id; + } + return image_id < rhs.image_id; + } +}; +WRITE_CLASS_ENCODER(ChildImageSpec); + +typedef std::set ChildImageSpecs; + struct GroupImageSpec { GroupImageSpec() {} @@ -182,7 +213,6 @@ struct GroupImageSpec { std::string image_key(); }; - WRITE_CLASS_ENCODER(GroupImageSpec); struct GroupImageStatus { diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index 1a2e96c37a2b3..0bd8d754bdf3f 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -2501,3 +2501,106 @@ TEST_F(TestClsRbd, op_features) ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features)); ASSERT_EQ(0u, features); } + +TEST_F(TestClsRbd, clone_parent) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string oid = get_temp_image_name(); + ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1)); + ASSERT_EQ(0, snapshot_add(&ioctx, oid, 123, "user_snap")); + + ASSERT_EQ(-ENOENT, child_attach(&ioctx, oid, 345, {})); + ASSERT_EQ(-ENOENT, child_detach(&ioctx, oid, 123, {})); + ASSERT_EQ(-ENOENT, child_detach(&ioctx, oid, 345, {})); + + ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {1, "image1"})); + ASSERT_EQ(-EEXIST, child_attach(&ioctx, oid, 123, {1, "image1"})); + ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {1, "image2"})); + ASSERT_EQ(0, child_attach(&ioctx, oid, 123, {2, "image2"})); + + std::vector snaps; + std::vector parents; + std::vector protection_status; + ASSERT_EQ(0, snapshot_get(&ioctx, oid, {123}, &snaps, + &parents, &protection_status)); + ASSERT_EQ(1U, snaps.size()); + ASSERT_EQ(3U, snaps[0].child_count); + + // op feature should have been enabled + uint64_t op_features; + ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features)); + ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_PARENT) == + RBD_OPERATION_FEATURE_CLONE_PARENT); + + // cannot attach to trashed snapshot + librados::ObjectWriteOperation op; + ::librbd::cls_client::snapshot_add(&op, 234, "trash_snap", + cls::rbd::TrashSnapshotNamespace()); + ASSERT_EQ(0, ioctx.operate(oid, &op)); + ASSERT_EQ(-ENOENT, child_attach(&ioctx, oid, 234, {})); + + cls::rbd::ChildImageSpecs child_images; + ASSERT_EQ(0, children_list(&ioctx, oid, 123, &child_images)); + + cls::rbd::ChildImageSpecs expected_child_images = { + {1, "image1"}, {1, "image2"}, {2, "image2"}}; + ASSERT_EQ(expected_child_images, child_images); + + expected_child_images = {{1, "image1"}, {2, "image2"}}; + ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {1, "image2"})); + ASSERT_EQ(0, children_list(&ioctx, oid, 123, &child_images)); + ASSERT_EQ(expected_child_images, child_images); + + ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {2, "image2"})); + + ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features)); + ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_PARENT) == + RBD_OPERATION_FEATURE_CLONE_PARENT); + + ASSERT_EQ(0, child_detach(&ioctx, oid, 123, {1, "image1"})); + ASSERT_EQ(-ENOENT, children_list(&ioctx, oid, 123, &child_images)); + + ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features)); + ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_PARENT) == 0ULL); +} + +TEST_F(TestClsRbd, clone_child) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string oid = get_temp_image_name(); + ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, + RBD_FEATURE_LAYERING | RBD_FEATURE_DEEP_FLATTEN, + oid, -1)); + ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "parent", 2}, 1)); + ASSERT_EQ(0, snapshot_add(&ioctx, oid, 123, "user_snap1")); + ASSERT_EQ(0, op_features_set(&ioctx, oid, RBD_OPERATION_FEATURE_CLONE_CHILD, + RBD_OPERATION_FEATURE_CLONE_CHILD)); + + // clone child should be disabled due to deep flatten + ASSERT_EQ(0, remove_parent(&ioctx, oid)); + uint64_t op_features; + ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features)); + ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_CHILD) == 0ULL); + + ASSERT_EQ(0, set_features(&ioctx, oid, 0, RBD_FEATURE_DEEP_FLATTEN)); + ASSERT_EQ(0, set_parent(&ioctx, oid, {1, "parent", 2}, 1)); + ASSERT_EQ(0, snapshot_add(&ioctx, oid, 124, "user_snap2")); + ASSERT_EQ(0, op_features_set(&ioctx, oid, RBD_OPERATION_FEATURE_CLONE_CHILD, + RBD_OPERATION_FEATURE_CLONE_CHILD)); + + // clone child should remain enabled w/o deep flatten + ASSERT_EQ(0, remove_parent(&ioctx, oid)); + ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features)); + ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_CHILD) == + RBD_OPERATION_FEATURE_CLONE_CHILD); + + // ... but removing the last linked snapshot should disable it + ASSERT_EQ(0, snapshot_remove(&ioctx, oid, 124)); + ASSERT_EQ(0, op_features_get(&ioctx, oid, &op_features)); + ASSERT_TRUE((op_features & RBD_OPERATION_FEATURE_CLONE_CHILD) == 0ULL); +} + diff --git a/src/test/encoding/types.h b/src/test/encoding/types.h index 9748345ffc6b0..ad206456003e6 100644 --- a/src/test/encoding/types.h +++ b/src/test/encoding/types.h @@ -415,6 +415,7 @@ TYPE(cls_rbd_parent) TYPE(cls_rbd_snap) #include "cls/rbd/cls_rbd_types.h" +TYPE(cls::rbd::ChildImageSpec) TYPE(cls::rbd::MirrorPeer) TYPE(cls::rbd::MirrorImage) TYPE(cls::rbd::MirrorImageMap) -- 2.39.5