]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: clone request now handles clone v2
authorJason Dillaman <dillaman@redhat.com>
Tue, 30 Jan 2018 01:09:46 +0000 (20:09 -0500)
committerJason Dillaman <dillaman@redhat.com>
Mon, 5 Feb 2018 16:12:00 +0000 (11:12 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
PendingReleaseNotes
src/librbd/ImageCtx.cc
src/librbd/ImageCtx.h
src/librbd/image/CloneRequest.cc
src/librbd/image/CloneRequest.h
src/test/librbd/image/test_mock_CloneRequest.cc
src/test/librbd/image/test_mock_RefreshRequest.cc

index 6ce397d470b6521b2985d1704175fd981d6dd484..173b4245bb4ffe279369f266d8552a7c50836af2 100644 (file)
 
   * The rbd CLI's "showmapped" JSON and XML output has changed.
 
+  * RBD now optionally supports simplified image clone semantics where
+    non-protected snapshots can be cloned; and snapshots with linked clones
+    can be removed and the space automatically reclaimed once all remaining
+    linked clones are detached. This feature is enabled by default if
+    the OSD "require-min-compat-client" flag is set to mimic or later; or can be
+    overridden via the "rbd_default_clone_format" configuration option.
+
 * The sample ``crush-location-hook`` script has been removed.  Its output is
   equivalent to the built-in default behavior, so it has been replaced with an
   example in the CRUSH documentation.
index ca4de1b3c1477e05edfe7589d166407070c9f34a..52568ee7908b4d352e70ecb6b636225c4b68e231 100644 (file)
@@ -649,6 +649,19 @@ struct C_InvalidateCache : public Context {
     return ((features & in_features) == in_features);
   }
 
+  bool ImageCtx::test_op_features(uint64_t in_op_features) const
+  {
+    RWLock::RLocker snap_locker(snap_lock);
+    return test_op_features(in_op_features, snap_lock);
+  }
+
+  bool ImageCtx::test_op_features(uint64_t in_op_features,
+                                  const RWLock &in_snap_lock) const
+  {
+    assert(snap_lock.is_locked());
+    return ((op_features & in_op_features) == in_op_features);
+  }
+
   int ImageCtx::get_flags(librados::snap_t _snap_id, uint64_t *_flags) const
   {
     assert(snap_lock.is_locked());
index d6978c23dba4d6ca8543c60b174afd887ecae277..5c7470f3048acd9bcdff8d3dbef7dd31b368194a 100644 (file)
@@ -278,6 +278,9 @@ namespace librbd {
     bool test_features(uint64_t test_features) const;
     bool test_features(uint64_t test_features,
                        const RWLock &in_snap_lock) const;
+    bool test_op_features(uint64_t op_features) const;
+    bool test_op_features(uint64_t op_features,
+                          const RWLock &in_snap_lock) const;
     int get_flags(librados::snap_t in_snap_id, uint64_t *flags) const;
     int test_flags(uint64_t test_flags, bool *flags_set) const;
     int test_flags(uint64_t test_flags, const RWLock &in_snap_lock,
index f0f850e1e796d8039998791c4f2875a6157b0c12..fa4b62e4714fd055aa660eee3bc144a7387f19e9 100644 (file)
@@ -83,15 +83,36 @@ void CloneRequest<I>::validate_options() {
     m_use_p_features = false;
   }
 
-  send_validate_parent();
+  std::string default_clone_format = m_cct->_conf->get_val<std::string>(
+    "rbd_default_clone_format");
+  if (default_clone_format == "1") {
+    m_clone_format = 1;
+  } else if (default_clone_format == "auto") {
+    librados::Rados rados(m_ioctx);
+    int8_t min_compat_client;
+    int8_t require_min_compat_client;
+    int r = rados.get_min_compatible_client(&min_compat_client,
+                                            &require_min_compat_client);
+    if (r < 0) {
+      complete(r);
+      return;
+    }
+    if (std::max(min_compat_client, require_min_compat_client) <
+          CEPH_RELEASE_MIMIC) {
+      m_clone_format = 1;
+    }
+  }
+
+  validate_parent();
 }
 
 template <typename I>
-void CloneRequest<I>::send_validate_parent() {
+void CloneRequest<I>::validate_parent() {
   ldout(m_cct, 20) << this << " " << __func__ << dendl;
 
   if (m_p_imctx->operations_disabled) {
-    lderr(m_cct) << "image operations disabled due to unsupported op features" << dendl;
+    lderr(m_cct) << "image operations disabled due to unsupported op features"
+                 << dendl;
     complete(-EROFS);
     return;
   }
@@ -128,7 +149,7 @@ void CloneRequest<I>::send_validate_parent() {
     return;
   }
 
-  if (!snap_protected) {
+  if (m_clone_format == 1 && !snap_protected) {
     lderr(m_cct) << "parent snapshot must be protected" << dendl;
     complete(-EINVAL);
     return;
@@ -142,12 +163,14 @@ void CloneRequest<I>::send_validate_child() {
   ldout(m_cct, 20) << this << " " << __func__ << dendl;
 
   using klass = CloneRequest<I>;
-  librados::AioCompletion *comp = create_rados_callback<klass, &klass::handle_validate_child>(this);
+  librados::AioCompletion *comp = create_rados_callback<
+    klass, &klass::handle_validate_child>(this);
 
   librados::ObjectReadOperation op;
   op.stat(NULL, NULL, NULL);
 
-  int r = m_ioctx.aio_operate(util::old_header_name(m_name), comp, &op, &m_out_bl);
+  int r = m_ioctx.aio_operate(util::old_header_name(m_name), comp, &op,
+                              &m_out_bl);
   assert(r == 0);
   comp->release();
 }
@@ -242,8 +265,7 @@ void CloneRequest<I>::send_set_parent() {
   using klass = CloneRequest<I>;
   librados::AioCompletion *comp =
     create_rados_callback<klass, &klass::handle_set_parent>(this);
-  int r = m_imctx->md_ctx.aio_operate(m_imctx->header_oid,
-                                                comp, &op);
+  int r = m_imctx->md_ctx.aio_operate(m_imctx->header_oid, comp, &op);
   assert(r == 0);
   comp->release();
 }
@@ -259,11 +281,75 @@ void CloneRequest<I>::handle_set_parent(int r) {
     return;
   }
 
-  send_add_child();
+  send_v2_set_op_feature();
+}
+
+template <typename I>
+void CloneRequest<I>::send_v2_set_op_feature() {
+  if (m_clone_format == 1) {
+    send_v1_add_child();
+    return;
+  }
+
+  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+
+  librados::ObjectWriteOperation op;
+  cls_client::op_features_set(&op, RBD_OPERATION_FEATURE_CLONE_CHILD,
+                              RBD_OPERATION_FEATURE_CLONE_CHILD);
+
+  auto aio_comp = create_rados_callback<
+    CloneRequest<I>, &CloneRequest<I>::handle_v2_set_op_feature>(this);
+  int r = m_ioctx.aio_operate(m_imctx->header_oid, aio_comp, &op);
+  assert(r == 0);
+  aio_comp->release();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_v2_set_op_feature(int r) {
+  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to enable clone v2: " << cpp_strerror(r) << dendl;
+    m_r_saved = r;
+    send_close();
+    return;
+  }
+
+  send_v2_child_attach();
+}
+
+template <typename I>
+void CloneRequest<I>::send_v2_child_attach() {
+  ldout(m_cct, 20) << this << " " << __func__ << dendl;
+
+  librados::ObjectWriteOperation op;
+  cls_client::child_attach(&op, m_p_imctx->snap_id,
+                           {m_imctx->md_ctx.get_id(), m_imctx->id});
+
+  auto aio_comp = create_rados_callback<
+    CloneRequest<I>, &CloneRequest<I>::handle_v2_child_attach>(this);
+  int r = m_p_imctx->md_ctx.aio_operate(m_p_imctx->header_oid, aio_comp, &op);
+  assert(r == 0);
+  aio_comp->release();
+}
+
+template <typename I>
+void CloneRequest<I>::handle_v2_child_attach(int r) {
+  ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(m_cct) << "failed to attach child image: " << cpp_strerror(r)
+                 << dendl;
+    m_r_saved = r;
+    send_close();
+    return;
+  }
+
+  send_metadata_list();
 }
 
 template <typename I>
-void CloneRequest<I>::send_add_child() {
+void CloneRequest<I>::send_v1_add_child() {
   ldout(m_cct, 20) << this << " " << __func__ << dendl;
 
   librados::ObjectWriteOperation op;
@@ -271,14 +357,14 @@ void CloneRequest<I>::send_add_child() {
 
   using klass = CloneRequest<I>;
   librados::AioCompletion *comp =
-    create_rados_callback<klass, &klass::handle_add_child>(this);
+    create_rados_callback<klass, &klass::handle_v1_add_child>(this);
   int r = m_ioctx.aio_operate(RBD_CHILDREN, comp, &op);
   assert(r == 0);
   comp->release();
 }
 
 template <typename I>
-void CloneRequest<I>::handle_add_child(int r) {
+void CloneRequest<I>::handle_v1_add_child(int r) {
   ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
 
   if (r < 0) {
@@ -288,22 +374,22 @@ void CloneRequest<I>::handle_add_child(int r) {
     return;
   }
 
-  send_refresh();
+  send_v1_refresh();
 }
 
 template <typename I>
-void CloneRequest<I>::send_refresh() {
+void CloneRequest<I>::send_v1_refresh() {
   ldout(m_cct, 20) << this << " " << __func__ << dendl;
 
   using klass = CloneRequest<I>;
   RefreshRequest<I> *req = RefreshRequest<I>::create(
     *m_imctx, false, false,
-    create_context_callback<klass, &klass::handle_refresh>(this));
+    create_context_callback<klass, &klass::handle_v1_refresh>(this));
   req->send();
 }
 
 template <typename I>
-void CloneRequest<I>::handle_refresh(int r) {
+void CloneRequest<I>::handle_v1_refresh(int r) {
   ldout(m_cct, 20) << this << " " << __func__ << " r=" << r << dendl;
 
   bool snap_protected = false;
index 75b294d90d1603bf00633a3a31027e6c5d6ec0cc..7c3d3ea44702b6a2577c58cfc442e743fe1d8eba 100644 (file)
@@ -40,46 +40,51 @@ private:
   /**
    * @verbatim
    *
-   *                                  <start>
-   *                                     |
-   *                                     v
-   *                             VALIDATE PARENT
-   *                                     |
-   *                                     v
-   * (error: bottom up)           VALIDATE CHILD
-   *  _______<_______                    |
-   * |               |                   v
-   * |               |             CREATE IMAGE
-   * |               |                   |
-   * |               |                   v          (parent_md exists)
-   * |               |              OPEN IMAGE. . . . . > . . . .
-   * v               |               /   |                      .
-   * |         REMOVE IMAGE<--------/    v                      .
-   * |               |           SET PARENT IN HEADER           .
-   * |          CLOSE IMAGE          /   |                      .
-   * |               ^-------<------/    v                      .
-   * |               |\           UPDATE DIR_CHILDREN. . < . . . 
-   * |               | \              /  |
-   * |               |  *<-----------/   v
-   * |               |                REFRESH
-   * |               |                   |
-   * |               |                   v            (meta is empty)
-   * |               |\         GET META IN PARENT  . . . . . . .
-   * |               | \              /  |                      .
-   * v               |  *<-----------/   v     (journaling disabled)           .
-   * |               |          SET META IN CHILD . . . . . . . v
-   * |               |               /   |                      .
-   * |               -------<-------/    v       (no need to enable mirror)             .
-   * |               |            GET MIRROR MODE . . . . . . . v
-   * |               |               /   |                      .
-   * |               -------<-------/    v                      .
-   * |               |          ENABLE MIRROR MODE              v
-   * |               |               /   |                      .
-   * |               -------<-------/    v                      .
-   * |                               CLOSE IMAGE . . . . .< . . .
-   * |                                   |
-   * |                                   v
-   * |_____________>__________________<finish>
+   * <start>
+   *    |
+   *    v
+   * VALIDATE CHILD
+   *    |
+   *    v
+   * CREATE CHILD                     <finish>
+   *    |                                 ^
+   *    v                                 |
+   * OPEN CHILD * * * * * * * * * * > REMOVE CHILD
+   *    |                                 ^
+   *    v                                 |
+   * SET PARENT * * * * * * * * * * > CLOSE IMAGE
+   *    |                               ^
+   *    |\--------\                     *
+   *    |         |                     *
+   *    |         v (clone v2 disabled) *
+   *    |     V1 ADD CHILD  * * * * * * ^
+   *    |         |                     *
+   *    |         v                     *
+   *    |     V1 VALIDATE PROTECTED * * ^
+   *    |         |                     *
+   *    v         |                     *
+   * V2 SET CLONE * * * * * * * * * * * ^
+   *    |         |                     *
+   *    v         |                     *
+   * V2 ATTACH CHILD  * * * * * * * * * *
+   *    |         |                     *
+   *    v         v                     *
+   * GET PARENT META  * * * * * * * * * ^
+   *    |                               *
+   *    v (skip if not needed)          *
+   * SET CHILD META * * * * * * * * * * ^
+   *    |                               *
+   *    v (skip if not needed)          *
+   * GET MIRROR MODE  * * * * * * * * * ^
+   *    |                               *
+   *    v (skip if not needed)          *
+   * SET MIRROR ENABLED * * * * * * * * *
+   *    |
+   *    v
+   * CLOSE IMAGE
+   *    |
+   *    v
+   * <finish>
    *
    * @endverbatim
    */
@@ -99,6 +104,7 @@ private:
   Context *m_on_finish;
 
   CephContext *m_cct;
+  uint32_t m_clone_format = 2;
   bool m_use_p_features;
   uint64_t m_p_features;
   uint64_t m_features;
@@ -109,8 +115,7 @@ private:
   int m_r_saved = 0;
 
   void validate_options();
-
-  void send_validate_parent();
+  void validate_parent();
 
   void send_validate_child();
   void handle_validate_child(int r);
@@ -124,11 +129,17 @@ private:
   void send_set_parent();
   void handle_set_parent(int r);
 
-  void send_add_child();
-  void handle_add_child(int r);
+  void send_v2_set_op_feature();
+  void handle_v2_set_op_feature(int r);
+
+  void send_v2_child_attach();
+  void handle_v2_child_attach(int r);
+
+  void send_v1_add_child();
+  void handle_v1_add_child(int r);
 
-  void send_refresh();
-  void handle_refresh(int r);
+  void send_v1_refresh();
+  void handle_v1_refresh(int r);
 
   void send_metadata_list();
   void handle_metadata_list(int r);
index 8a0f543dff33323f4547db2b7902b07c041ad4ab..fc223088f6db5c2d96e36cbd092b7aa975e9bf77 100644 (file)
@@ -168,6 +168,8 @@ public:
   void SetUp() override {
     TestMockFixture::SetUp();
 
+    ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "2"));
+
     ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
     ASSERT_EQ(0, image_ctx->operations->snap_create(
                    cls::rbd::UserSnapshotNamespace{}, "snap"));
@@ -182,6 +184,16 @@ public:
     }
   }
 
+  void expect_get_min_compat_client(int8_t min_compat_client, int r) {
+    auto mock_rados_client = get_mock_io_ctx(m_ioctx).get_mock_rados_client();
+    EXPECT_CALL(*mock_rados_client, get_min_compatible_client(_, _))
+      .WillOnce(Invoke([min_compat_client, r](int8_t* min, int8_t* required_min) {
+                  *min = min_compat_client;
+                  *required_min = min_compat_client;
+                  return r;
+                }));
+  }
+
   void expect_get_image_size(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
                              uint64_t size) {
     EXPECT_CALL(mock_image_ctx, get_image_size(snap_id))
@@ -220,6 +232,29 @@ public:
                 }));
   }
 
+  void expect_op_features_set(librados::IoCtx& io_ctx,
+                              const std::string& clone_id, int r) {
+    bufferlist bl;
+    encode(static_cast<uint64_t>(RBD_OPERATION_FEATURE_CLONE_CHILD), bl);
+    encode(static_cast<uint64_t>(RBD_OPERATION_FEATURE_CLONE_CHILD), bl);
+
+    EXPECT_CALL(get_mock_io_ctx(io_ctx),
+                exec(util::header_name(clone_id), _, StrEq("rbd"),
+                     StrEq("op_features_set"), ContentsEqual(bl), _, _))
+      .WillOnce(Return(r));
+  }
+
+  void expect_child_attach(MockImageCtx &mock_image_ctx, int r) {
+    bufferlist bl;
+    encode(mock_image_ctx.snap_id, bl);
+    encode(cls::rbd::ChildImageSpec{m_ioctx.get_id(), mock_image_ctx.id}, bl);
+
+    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+                     StrEq("child_attach"), ContentsEqual(bl), _, _))
+      .WillOnce(Return(r));
+  }
+
   void expect_add_child(librados::IoCtx& io_ctx, int r) {
     EXPECT_CALL(get_mock_io_ctx(io_ctx),
                 exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("add_child"), _, _, _))
@@ -306,13 +341,104 @@ public:
   librbd::ImageCtx *image_ctx;
 };
 
-TEST_F(TestMockImageCloneRequest, Success) {
+TEST_F(TestMockImageCloneRequest, SuccessV1) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+  ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "1"));
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, 0);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  expect_metadata_list(mock_image_ctx, {{"key", {}}}, 0);
+  expect_metadata_set(m_ioctx, mock_image_ctx, {{"key", {}}}, 0);
+
+  MockMirrorEnableRequest mock_mirror_enable_request;
+  if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
+    expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+    expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+    expect_mirror_enable(mock_mirror_enable_request, 0);
+  } else {
+    expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+  }
+
+  expect_close(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, SuccessV2) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+
+  expect_op_features_set(m_ioctx, mock_image_ctx.id, 0);
+  expect_child_attach(mock_image_ctx, 0);
+
+  expect_metadata_list(mock_image_ctx, {{"key", {}}}, 0);
+  expect_metadata_set(m_ioctx, mock_image_ctx, {{"key", {}}}, 0);
+
+  MockMirrorEnableRequest mock_mirror_enable_request;
+  if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
+    expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+    expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+    expect_mirror_enable(mock_mirror_enable_request, 0);
+  } else {
+    expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+  }
+
+  expect_close(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, SuccessAuto) {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+  ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "auto"));
 
   MockTestImageCtx mock_image_ctx(*image_ctx);
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_get_min_compat_client(1, 0);
   expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
   expect_is_snap_protected(mock_image_ctx, true, 0);
 
@@ -431,6 +557,7 @@ TEST_F(TestMockImageCloneRequest, SetParentError) {
 
 TEST_F(TestMockImageCloneRequest, AddChildError) {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+  ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "1"));
 
   MockTestImageCtx mock_image_ctx(*image_ctx);
   expect_op_work_queue(mock_image_ctx);
@@ -461,6 +588,7 @@ TEST_F(TestMockImageCloneRequest, AddChildError) {
 
 TEST_F(TestMockImageCloneRequest, RefreshError) {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+  ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "1"));
 
   MockTestImageCtx mock_image_ctx(*image_ctx);
   expect_op_work_queue(mock_image_ctx);
@@ -495,6 +623,7 @@ TEST_F(TestMockImageCloneRequest, RefreshError) {
 
 TEST_F(TestMockImageCloneRequest, MetadataListError) {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+  ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "1"));
 
   MockTestImageCtx mock_image_ctx(*image_ctx);
   expect_op_work_queue(mock_image_ctx);
@@ -545,11 +674,9 @@ TEST_F(TestMockImageCloneRequest, MetadataSetError) {
 
   expect_open(mock_image_ctx, 0);
   expect_set_parent(mock_image_ctx, 0);
-  expect_add_child(m_ioctx, 0);
 
-  MockRefreshRequest mock_refresh_request;
-  expect_refresh(mock_refresh_request, 0);
-  expect_is_snap_protected(mock_image_ctx, true, 0);
+  expect_op_features_set(m_ioctx, mock_image_ctx.id, 0);
+  expect_child_attach(mock_image_ctx, 0);
 
   expect_metadata_list(mock_image_ctx, {{"key", {}}}, 0);
   expect_metadata_set(m_ioctx, mock_image_ctx, {{"key", {}}}, -EINVAL);
@@ -570,6 +697,7 @@ TEST_F(TestMockImageCloneRequest, MetadataSetError) {
 
 TEST_F(TestMockImageCloneRequest, GetMirrorModeError) {
   REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING);
+  ASSERT_EQ(0, _rados.conf_set("rbd_default_clone_format", "1"));
 
   MockTestImageCtx mock_image_ctx(*image_ctx);
   expect_op_work_queue(mock_image_ctx);
@@ -623,11 +751,9 @@ TEST_F(TestMockImageCloneRequest, MirrorEnableError) {
 
   expect_open(mock_image_ctx, 0);
   expect_set_parent(mock_image_ctx, 0);
-  expect_add_child(m_ioctx, 0);
 
-  MockRefreshRequest mock_refresh_request;
-  expect_refresh(mock_refresh_request, 0);
-  expect_is_snap_protected(mock_image_ctx, true, 0);
+  expect_op_features_set(m_ioctx, mock_image_ctx.id, 0);
+  expect_child_attach(mock_image_ctx, 0);
 
   expect_metadata_list(mock_image_ctx, {}, 0);
 
@@ -666,11 +792,9 @@ TEST_F(TestMockImageCloneRequest, CloseError) {
 
   expect_open(mock_image_ctx, 0);
   expect_set_parent(mock_image_ctx, 0);
-  expect_add_child(m_ioctx, 0);
 
-  MockRefreshRequest mock_refresh_request;
-  expect_refresh(mock_refresh_request, 0);
-  expect_is_snap_protected(mock_image_ctx, true, 0);
+  expect_op_features_set(m_ioctx, mock_image_ctx.id, 0);
+  expect_child_attach(mock_image_ctx, 0);
 
   expect_metadata_list(mock_image_ctx, {}, 0);
   expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
index eb8ffa5591f0699d2ffc38acc8da6e2332742d0f..5ec7a8f54c1bd312f0f19f07701bb0777b055980 100644 (file)
@@ -597,10 +597,11 @@ TEST_F(TestMockImageRefreshRequest, SuccessChild) {
   expect_test_features(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+  expect_get_mutable_metadata(mock_image_ctx, ictx2->features, 0);
   expect_get_metadata(mock_image_ctx, 0);
   expect_apply_metadata(mock_image_ctx, 0);
   expect_get_flags(mock_image_ctx, 0);
+  expect_get_op_features(mock_image_ctx, RBD_OPERATION_FEATURE_CLONE_CHILD, 0);
   expect_get_group(mock_image_ctx, 0);
   expect_refresh_parent_is_required(*mock_refresh_parent_request, true);
   expect_refresh_parent_send(mock_image_ctx, *mock_refresh_parent_request, 0);
@@ -649,10 +650,11 @@ TEST_F(TestMockImageRefreshRequest, SuccessChildDontOpenParent) {
   expect_test_features(mock_image_ctx);
 
   InSequence seq;
-  expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
+  expect_get_mutable_metadata(mock_image_ctx, ictx2->features, 0);
   expect_get_metadata(mock_image_ctx, 0);
   expect_apply_metadata(mock_image_ctx, 0);
   expect_get_flags(mock_image_ctx, 0);
+  expect_get_op_features(mock_image_ctx, RBD_OPERATION_FEATURE_CLONE_CHILD, 0);
   expect_get_group(mock_image_ctx, 0);
   if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
     expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);