]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: store/remove image state when creating/removing mirror snapshot
authorMykola Golub <mgolub@suse.com>
Thu, 21 Nov 2019 14:26:34 +0000 (14:26 +0000)
committerMykola Golub <mgolub@suse.com>
Tue, 10 Dec 2019 15:45:30 +0000 (15:45 +0000)
Signed-off-by: Mykola Golub <mgolub@suse.com>
src/librbd/mirror/snapshot/CreateNonPrimaryRequest.cc
src/librbd/mirror/snapshot/CreateNonPrimaryRequest.h
src/librbd/operation/SnapshotCreateRequest.cc
src/librbd/operation/SnapshotCreateRequest.h
src/librbd/operation/SnapshotRemoveRequest.cc
src/librbd/operation/SnapshotRemoveRequest.h
src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc
src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc
src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc

index 305cb32d6662dff2b541928f2e760103fffaa853..215499a1d44172640a03981ab25614dc712edbf3 100644 (file)
@@ -9,6 +9,7 @@
 #include "librbd/ImageState.h"
 #include "librbd/Operations.h"
 #include "librbd/Utils.h"
+#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
 
 #define dout_subsys ceph_subsys_rbd
 
@@ -136,11 +137,45 @@ void CreateNonPrimaryRequest<I>::handle_create_snapshot(int r) {
     return;
   }
 
-  if (m_snap_id != nullptr) {
+  write_image_state();
+}
+
+template <typename I>
+void CreateNonPrimaryRequest<I>::write_image_state() {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+  uint64_t snap_id;
+  {
     std::shared_lock image_locker{m_image_ctx->image_lock};
     cls::rbd::MirrorNonPrimarySnapshotNamespace ns{m_primary_mirror_uuid,
                                                    m_primary_snap_id};
-    *m_snap_id = m_image_ctx->get_snap_id(ns, m_snap_name);
+    snap_id = m_image_ctx->get_snap_id(ns, m_snap_name);
+  }
+
+  if (m_snap_id != nullptr) {
+    *m_snap_id = snap_id;
+  }
+
+  auto ctx = create_context_callback<
+    CreateNonPrimaryRequest<I>,
+    &CreateNonPrimaryRequest<I>::handle_write_image_state>(this);
+
+  auto req = WriteImageStateRequest<I>::create(m_image_ctx, snap_id,
+                                               m_image_state, ctx);
+  req->send();
+}
+
+template <typename I>
+void CreateNonPrimaryRequest<I>::handle_write_image_state(int r) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << "failed to write image state: " << cpp_strerror(r)
+               << dendl;
+    finish(r);
+    return;
   }
 
   finish(0);
index 6d916c47414688f5ac56c2ad67f27320fbf4ac45..60f56f344bec2af1742136bc924db3b162a73b5e 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "include/buffer.h"
 #include "cls/rbd/cls_rbd_types.h"
+#include "librbd/mirror/snapshot/Types.h"
 
 #include <string>
 #include <set>
@@ -25,19 +26,22 @@ public:
   static CreateNonPrimaryRequest *create(ImageCtxT *image_ctx,
                                          const std::string &primary_mirror_uuid,
                                          uint64_t primary_snap_id,
+                                         const ImageState &image_state,
                                          uint64_t *snap_id,
                                          Context *on_finish) {
     return new CreateNonPrimaryRequest(image_ctx, primary_mirror_uuid,
-                                       primary_snap_id, snap_id, on_finish);
+                                       primary_snap_id, image_state, snap_id,
+                                       on_finish);
   }
 
   CreateNonPrimaryRequest(ImageCtxT *image_ctx,
                           const std::string &primary_mirror_uuid,
-                          uint64_t primary_snap_id, uint64_t *snap_id,
+                          uint64_t primary_snap_id,
+                          const ImageState &image_state, uint64_t *snap_id,
                           Context *on_finish)
     : m_image_ctx(image_ctx), m_primary_mirror_uuid(primary_mirror_uuid),
-      m_primary_snap_id(primary_snap_id), m_snap_id(snap_id),
-      m_on_finish(on_finish) {
+      m_primary_snap_id(primary_snap_id), m_image_state(image_state),
+      m_snap_id(snap_id), m_on_finish(on_finish) {
   }
 
   void send();
@@ -58,6 +62,9 @@ private:
    * CREATE_SNAPSHOT
    *    |
    *    v
+   * WRITE_IMAGE_STATE
+   *    |
+   *    v
    * <finish>
    *
    * @endverbatim
@@ -66,6 +73,7 @@ private:
   ImageCtxT *m_image_ctx;
   std::string m_primary_mirror_uuid;
   uint64_t m_primary_snap_id;
+  ImageState m_image_state;
   uint64_t *m_snap_id;
   Context *m_on_finish;
 
@@ -85,6 +93,9 @@ private:
   void create_snapshot();
   void handle_create_snapshot(int r);
 
+  void write_image_state();
+  void handle_write_image_state(int r);
+
   void finish(int r);
 
   bool validate_snapshot();
index c949a7958fc0ec1b298ba34f0dee8a3b47a9ca34..c0452458d46660d84e07027a25dd02039ab99a34 100644 (file)
@@ -10,6 +10,7 @@
 #include "librbd/ObjectMap.h"
 #include "librbd/Utils.h"
 #include "librbd/io/ImageRequestWQ.h"
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
@@ -220,9 +221,7 @@ Context *SnapshotCreateRequest<I>::send_create_object_map() {
   if (image_ctx.object_map == nullptr || m_skip_object_map) {
     image_ctx.image_lock.unlock_shared();
 
-    update_snap_context();
-    image_ctx.io_work_queue->unblock_writes();
-    return this->create_context_finisher(0);
+    return send_create_image_state();
   }
 
   CephContext *cct = image_ctx.cct;
@@ -242,6 +241,46 @@ Context *SnapshotCreateRequest<I>::handle_create_object_map(int *result) {
   CephContext *cct = image_ctx.cct;
   ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl;
 
+  if (*result < 0) {
+    lderr(cct) << this << " " << __func__ << ": failed to snapshot object map: "
+               << cpp_strerror(*result) << dendl;
+
+    update_snap_context();
+    image_ctx.io_work_queue->unblock_writes();
+    return this->create_context_finisher(*result);
+  }
+
+  return send_create_image_state();
+}
+
+template <typename I>
+Context *SnapshotCreateRequest<I>::send_create_image_state() {
+  I &image_ctx = this->m_image_ctx;
+  auto type = cls::rbd::get_snap_namespace_type(m_snap_namespace);
+
+  if (type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY) {
+    update_snap_context();
+    image_ctx.io_work_queue->unblock_writes();
+    return this->create_context_finisher(0);
+  }
+
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << dendl;
+
+  auto req = mirror::snapshot::SetImageStateRequest<I>::create(
+      &image_ctx, m_snap_id, create_context_callback<
+      SnapshotCreateRequest<I>,
+      &SnapshotCreateRequest<I>::handle_create_image_state>(this));
+  req->send();
+  return nullptr;
+}
+
+template <typename I>
+Context *SnapshotCreateRequest<I>::handle_create_image_state(int *result) {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl;
+
   update_snap_context();
   image_ctx.io_work_queue->unblock_writes();
   if (*result < 0) {
index 406d2f01c47e6380f9870bacf9485c27a140f44b..1754f26bfba288cce458584f8e08104b67b86c7d 100644 (file)
@@ -40,13 +40,16 @@ public:
    *   . . . > STATE_ALLOCATE_SNAP_ID                    *
    *   .           |                                     *
    *   .           v                                     *
-   *   . . . . STATE_CREATE_SNAP * * * * * * * * * *     *
-   *               |                               *     *
-   *               v                               *     *
-   *           STATE_CREATE_OBJECT_MAP (skip if    *     *
-   *               |                    disabled)  *     *
-   *               |                               *     *
-   *               |                               v     *
+   *   . . . . STATE_CREATE_SNAP * * * * * * * * * * *   *
+   *               |                                 *   *
+   *               v                                 *   *
+   *           STATE_CREATE_OBJECT_MAP (skip if      *   *
+   *               |                    disabled)    *   *
+   *               v                                 *   *
+   *           STATE_CREATE_IMAGE_STATE (skip if     *   *
+   *               |                     not mirror  *   *
+   *               |                     snapshot)   *   *
+   *               |                                 v   *
    *               |              STATE_RELEASE_SNAP_ID  *
    *               |                     |               *
    *               |                     v               *
@@ -106,6 +109,9 @@ private:
   Context *send_create_object_map();
   Context *handle_create_object_map(int *result);
 
+  Context *send_create_image_state();
+  Context *handle_create_image_state(int *result);
+
   void send_release_snap_id();
   Context *handle_release_snap_id(int *result);
 
index e45068f3c8ee812a1692206eec80845288289055..da98f6be6f8a50e493bafb65b386c761b6c804d9 100644 (file)
@@ -10,6 +10,7 @@
 #include "librbd/ObjectMap.h"
 #include "librbd/Utils.h"
 #include "librbd/image/DetachChildRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
@@ -332,7 +333,7 @@ void SnapshotRemoveRequest<I>::remove_object_map() {
   }
 
   // object map disabled
-  release_snap_id();
+  remove_image_state();
 }
 
 template <typename I>
@@ -348,6 +349,44 @@ void SnapshotRemoveRequest<I>::handle_remove_object_map(int r) {
     return;
   }
 
+  remove_image_state();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::remove_image_state() {
+  I &image_ctx = this->m_image_ctx;
+  auto type = cls::rbd::get_snap_namespace_type(m_snap_namespace);
+
+  if (type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY &&
+      type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY) {
+    release_snap_id();
+    return;
+  }
+
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 5) << dendl;
+
+  auto ctx = create_context_callback<
+    SnapshotRemoveRequest<I>,
+    &SnapshotRemoveRequest<I>::handle_remove_image_state>(this);
+  auto req = mirror::snapshot::RemoveImageStateRequest<I>::create(
+    &image_ctx, m_snap_id, ctx);
+  req->send();
+}
+
+template <typename I>
+void SnapshotRemoveRequest<I>::handle_remove_image_state(int r) {
+  I &image_ctx = this->m_image_ctx;
+  CephContext *cct = image_ctx.cct;
+  ldout(cct, 5) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << "failed to remove image state: " << cpp_strerror(r)
+               << dendl;
+    this->complete(r);
+    return;
+  }
+
   release_snap_id();
 }
 
index a47bbc0a377aade83a36b30f8b9301ea0cdf7e18..17638a52917e7a4bbd979c33403bf0bb9b07d4d5 100644 (file)
@@ -43,6 +43,9 @@ public:
    *    v (skip if disabled/in-use)
    * REMOVE_OBJECT_MAP
    *    |
+   *    v (skip if not mirror snpashot)
+   * REMOVE_IMAGE_STATE
+   *    |
    *    v (skip if in-use)
    * RELEASE_SNAP_ID
    *    |
@@ -103,6 +106,9 @@ private:
   void remove_object_map();
   void handle_remove_object_map(int r);
 
+  void remove_image_state();
+  void handle_remove_image_state(int r);
+
   void release_snap_id();
   void handle_release_snap_id(int r);
 
index 9a3a1ab6faa4f229ffab00b019e82a87173ed40c..03bdac2503a8c20c6ca8eb868f0526c2851c20f5 100644 (file)
@@ -8,6 +8,7 @@
 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
 #include "test/librados_test_stub/MockTestMemRadosClient.h"
 #include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
+#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
 
 namespace librbd {
 
@@ -19,6 +20,37 @@ struct MockTestImageCtx : public MockImageCtx {
 };
 
 } // anonymous namespace
+
+namespace mirror {
+namespace snapshot {
+template <>
+struct WriteImageStateRequest<MockTestImageCtx> {
+  uint64_t snap_id = CEPH_NOSNAP;
+  ImageState image_state;
+  Context* on_finish = nullptr;
+  static WriteImageStateRequest* s_instance;
+  static WriteImageStateRequest *create(MockTestImageCtx *image_ctx,
+                                        uint64_t snap_id,
+                                        const ImageState &image_state,
+                                        Context *on_finish) {
+    ceph_assert(s_instance != nullptr);
+    s_instance->snap_id = snap_id;
+    s_instance->image_state = image_state;
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  WriteImageStateRequest() {
+    s_instance = this;
+  }
+};
+
+WriteImageStateRequest<MockTestImageCtx>* WriteImageStateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace snapshot
+} // namespace mirror
 } // namespace librbd
 
 // template definitions
@@ -32,6 +64,7 @@ namespace snapshot {
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::InSequence;
+using ::testing::Invoke;
 using ::testing::Return;
 using ::testing::StrEq;
 using ::testing::WithArg;
@@ -39,6 +72,7 @@ using ::testing::WithArg;
 class TestMockMirrorSnapshotCreateNonPrimaryRequest : public TestMockFixture {
 public:
   typedef CreateNonPrimaryRequest<MockTestImageCtx> MockCreateNonPrimaryRequest;
+  typedef WriteImageStateRequest<MockTestImageCtx> MockWriteImageStateRequest;
 
   uint64_t m_snap_seq = 0;
 
@@ -79,6 +113,18 @@ public:
       .WillOnce(WithArg<2>(CompleteContext(
                              r, mock_image_ctx.image_ctx->op_work_queue)));
   }
+
+  void expect_write_image_state(
+      MockTestImageCtx &mock_image_ctx,
+      MockWriteImageStateRequest &mock_write_image_state_request, int r) {
+    EXPECT_CALL(mock_image_ctx, get_snap_id(_, _))
+      .WillOnce(Return(123));
+    EXPECT_CALL(mock_write_image_state_request, send())
+      .WillOnce(Invoke([&mock_image_ctx, &mock_write_image_state_request, r]() {
+                         mock_image_ctx.image_ctx->op_work_queue->queue(
+                           mock_write_image_state_request.on_finish, r);
+                       }));
+  }
 };
 
 TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, Success) {
@@ -96,10 +142,12 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, Success) {
     mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
                      cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
   expect_create_snapshot(mock_image_ctx, 0);
+  MockWriteImageStateRequest mock_write_image_state_request;
+  expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -118,7 +166,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, RefreshError) {
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -140,7 +188,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, GetMirrorImageError) {
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -163,7 +211,33 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, CreateSnapshotError) {
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, WriteImageStateError) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+
+  InSequence seq;
+
+  expect_refresh_image(mock_image_ctx, true, 0);
+  expect_get_mirror_image(
+    mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
+                     cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
+  expect_create_snapshot(mock_image_ctx, 0);
+  MockWriteImageStateRequest mock_write_image_state_request;
+  expect_write_image_state(mock_image_ctx, mock_write_image_state_request,
+                           -EINVAL);
+
+  C_SaferCond ctx;
+  auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -187,7 +261,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidateErrorPrimary) {
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -209,10 +283,12 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidatePrimaryDemoted) {
     mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
                      cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
   expect_create_snapshot(mock_image_ctx, 0);
+  MockWriteImageStateRequest mock_write_image_state_request;
+  expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
@@ -236,7 +312,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidateErrorNonPrimaryNot
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -259,10 +335,12 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidateNonPrimaryCopied)
     mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid",
                      cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
   expect_create_snapshot(mock_image_ctx, 0);
+  MockWriteImageStateRequest mock_write_image_state_request;
+  expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0);
 
   C_SaferCond ctx;
   auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid",
-                                             123, nullptr, &ctx);
+                                             123, {}, nullptr, &ctx);
   req->send();
   ASSERT_EQ(0, ctx.wait());
 }
index 52fcd080dfc6ab5fb063072998463fdbd9f6d2d6..42152157f24d081e2daaa7542aaae74866505d16 100644 (file)
@@ -8,10 +8,41 @@
 #include "common/bit_vector.hpp"
 #include "librbd/internal.h"
 #include "librbd/ObjectMap.h"
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
 #include "librbd/operation/SnapshotCreateRequest.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+template<>
+class SetImageStateRequest<MockImageCtx> {
+public:
+  static SetImageStateRequest *s_instance;
+  Context *on_finish = nullptr;
+
+  static SetImageStateRequest *create(MockImageCtx *image_ctx, uint64_t snap_id,
+                                      Context *on_finish) {
+    ceph_assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  SetImageStateRequest() {
+    s_instance = this;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
+SetImageStateRequest<MockImageCtx> *SetImageStateRequest<MockImageCtx>::s_instance;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
 // template definitions
 #include "librbd/operation/SnapshotCreateRequest.cc"
 
@@ -29,6 +60,7 @@ using ::testing::WithArg;
 class TestMockOperationSnapshotCreateRequest : public TestMockFixture {
 public:
   typedef SnapshotCreateRequest<MockImageCtx> MockSnapshotCreateRequest;
+  typedef mirror::snapshot::SetImageStateRequest<MockImageCtx> MockSetImageStateRequest;
 
   void expect_block_writes(MockImageCtx &mock_image_ctx) {
     EXPECT_CALL(*mock_image_ctx.io_work_queue, block_writes(_))
@@ -85,6 +117,14 @@ public:
     }
   }
 
+  void expect_set_image_state(
+      MockImageCtx &mock_image_ctx,
+      MockSetImageStateRequest &mock_set_image_state_request, int r) {
+    EXPECT_CALL(mock_set_image_state_request, send())
+      .WillOnce(FinishRequest(&mock_set_image_state_request, r,
+                              &mock_image_ctx));
+  }
+
   void expect_update_snap_context(MockImageCtx &mock_image_ctx) {
     // state machine checks to ensure a refresh hasn't already added the snap
     EXPECT_CALL(mock_image_ctx, get_snap_info(_))
@@ -96,7 +136,6 @@ public:
     EXPECT_CALL(*mock_image_ctx.io_work_queue, unblock_writes())
                   .Times(1);
   }
-
 };
 
 TEST_F(TestMockOperationSnapshotCreateRequest, Success) {
@@ -309,5 +348,47 @@ TEST_F(TestMockOperationSnapshotCreateRequest, SkipObjectMap) {
   ASSERT_EQ(0, cond_ctx.wait());
 }
 
+TEST_F(TestMockOperationSnapshotCreateRequest, SetImageState) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+
+  MockExclusiveLock mock_exclusive_lock;
+  if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+    mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+  }
+
+  MockObjectMap mock_object_map;
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    mock_image_ctx.object_map = &mock_object_map;
+  }
+
+  expect_verify_lock_ownership(mock_image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  ::testing::InSequence seq;
+  expect_block_writes(mock_image_ctx);
+  expect_allocate_snap_id(mock_image_ctx, 0);
+  expect_snap_create(mock_image_ctx, 0);
+  expect_object_map_snap_create(mock_image_ctx);
+  MockSetImageStateRequest mock_set_image_state_request;
+  expect_set_image_state(mock_image_ctx, mock_set_image_state_request, 0);
+  expect_update_snap_context(mock_image_ctx);
+  expect_unblock_writes(mock_image_ctx);
+
+  C_SaferCond cond_ctx;
+  MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest(
+    mock_image_ctx, &cond_ctx, cls::rbd::MirrorPrimarySnapshotNamespace(),
+      "snap1", 0, false);
+  {
+    std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+    req->send();
+  }
+  ASSERT_EQ(0, cond_ctx.wait());
+}
+
 } // namespace operation
 } // namespace librbd
index 0665ee84a69892414b64bc05ca772b97884dea9b..2139496cc3b5e4492000a09d9347cd45d15b1c6e 100644 (file)
 #include "librbd/internal.h"
 #include "librbd/Operations.h"
 #include "librbd/image/DetachChildRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
 #include "librbd/operation/SnapshotRemoveRequest.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
-// template definitions
-#include "librbd/operation/SnapshotRemoveRequest.cc"
-
 namespace librbd {
 namespace image {
 
@@ -44,6 +42,40 @@ DetachChildRequest<MockImageCtx> *DetachChildRequest<MockImageCtx>::s_instance;
 
 } // namespace image
 
+namespace mirror {
+namespace snapshot {
+
+template<>
+class RemoveImageStateRequest<MockImageCtx> {
+public:
+  static RemoveImageStateRequest *s_instance;
+  Context *on_finish = nullptr;
+
+  static RemoveImageStateRequest *create(MockImageCtx *image_ctx,
+                                         uint64_t snap_id,
+                                         Context *on_finish) {
+    ceph_assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  RemoveImageStateRequest() {
+    s_instance = this;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
+RemoveImageStateRequest<MockImageCtx> *RemoveImageStateRequest<MockImageCtx>::s_instance;
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/operation/SnapshotRemoveRequest.cc"
+
+namespace librbd {
 namespace operation {
 
 using ::testing::_;
@@ -59,6 +91,7 @@ class TestMockOperationSnapshotRemoveRequest : public TestMockFixture {
 public:
   typedef SnapshotRemoveRequest<MockImageCtx> MockSnapshotRemoveRequest;
   typedef image::DetachChildRequest<MockImageCtx> MockDetachChildRequest;
+  typedef mirror::snapshot::RemoveImageStateRequest<MockImageCtx> MockRemoveImageStateRequest;
 
   int create_snapshot(const char *snap_name) {
     librbd::ImageCtx *ictx;
@@ -150,6 +183,14 @@ public:
     }
   }
 
+  void expect_remove_image_state(
+      MockImageCtx &mock_image_ctx,
+      MockRemoveImageStateRequest &mock_remove_image_state_request, int r) {
+    EXPECT_CALL(mock_remove_image_state_request, send())
+      .WillOnce(FinishRequest(&mock_remove_image_state_request, r,
+                              &mock_image_ctx));
+  }
+
   void expect_get_parent_spec(MockImageCtx &mock_image_ctx, int r) {
     if (mock_image_ctx.old_format) {
       return;
@@ -444,6 +485,55 @@ TEST_F(TestMockOperationSnapshotRemoveRequest, TrashCloneParent) {
   ASSERT_EQ(-EBUSY, cond_ctx.wait());
 }
 
+TEST_F(TestMockOperationSnapshotRemoveRequest, MirrorSnapshot) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, snap_create(*ictx, "snap1"));
+  ASSERT_EQ(0, ictx->state->refresh_if_required());
+
+  MockImageCtx mock_image_ctx(*ictx);
+
+  MockExclusiveLock mock_exclusive_lock;
+  if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+    mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+  }
+
+  MockObjectMap mock_object_map;
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    mock_image_ctx.object_map = &mock_object_map;
+  }
+
+  expect_op_work_queue(mock_image_ctx);
+
+  ::testing::InSequence seq;
+  expect_snapshot_trash_add(mock_image_ctx, 0);
+
+  uint64_t snap_id = ictx->snap_info.rbegin()->first;
+  expect_snapshot_get(mock_image_ctx,
+                      {snap_id, {cls::rbd::MirrorPrimarySnapshotNamespace{}},
+                       "mirror", 123, {}, 0}, 0);
+
+  expect_get_parent_spec(mock_image_ctx, 0);
+  expect_object_map_snap_remove(mock_image_ctx, 0);
+  MockRemoveImageStateRequest mock_remove_image_state_request;
+  expect_remove_image_state(mock_image_ctx, mock_remove_image_state_request, 0);
+  expect_release_snap_id(mock_image_ctx);
+  expect_snap_remove(mock_image_ctx, 0);
+  expect_rm_snap(mock_image_ctx);
+
+  C_SaferCond cond_ctx;
+  MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
+    mock_image_ctx, &cond_ctx, cls::rbd::MirrorPrimarySnapshotNamespace(),
+    "mirror", snap_id);
+  {
+    std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+    req->send();
+  }
+  ASSERT_EQ(0, cond_ctx.wait());
+}
+
 TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotTrashAddNotSupported) {
   REQUIRE_FORMAT_V2();