]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
librbd: state machine for unlinking peer from mirror snapshot
authorMykola Golub <mgolub@suse.com>
Mon, 7 Oct 2019 16:07:34 +0000 (17:07 +0100)
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/CMakeLists.txt
src/librbd/mirror/snapshot/UnlinkPeerRequest.cc [new file with mode: 0644]
src/librbd/mirror/snapshot/UnlinkPeerRequest.h [new file with mode: 0644]
src/test/librbd/CMakeLists.txt
src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc [new file with mode: 0644]
src/test/librbd/test_mirroring.cc

index 8b95fe1e30d19c04eb1a7985acf88cf8b380e049..ee869e66014b74a3878a8ab5c6ed710d3dd27ef9 100644 (file)
@@ -104,6 +104,7 @@ set(librbd_internal_srcs
   mirror/GetInfoRequest.cc
   mirror/GetStatusRequest.cc
   mirror/PromoteRequest.cc
+  mirror/snapshot/UnlinkPeerRequest.cc
   object_map/CreateRequest.cc
   object_map/InvalidateRequest.cc
   object_map/LockRequest.cc
diff --git a/src/librbd/mirror/snapshot/UnlinkPeerRequest.cc b/src/librbd/mirror/snapshot/UnlinkPeerRequest.cc
new file mode 100644 (file)
index 0000000..ec23f35
--- /dev/null
@@ -0,0 +1,215 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/Utils.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::mirror::snapshot::UnlinkPeerRequest: " \
+                           << this << " " << __func__ << ": "
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using librbd::util::create_context_callback;
+using librbd::util::create_rados_callback;
+
+template <typename I>
+void UnlinkPeerRequest<I>::send() {
+  if (!m_image_ctx->state->is_refresh_required()) {
+    unlink_peer();
+    return;
+  }
+
+  refresh_image();
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::refresh_image() {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+  auto ctx = create_context_callback<
+    UnlinkPeerRequest<I>, &UnlinkPeerRequest<I>::handle_refresh_image>(this);
+  m_image_ctx->state->refresh(ctx);
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::handle_refresh_image(int r) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << "r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << "failed to refresh image: " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  unlink_peer();
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::unlink_peer() {
+  CephContext *cct = m_image_ctx->cct;
+
+  m_image_ctx->image_lock.lock_shared();
+
+  auto snap_info = m_image_ctx->get_snap_info(m_snap_id);
+  if (!snap_info) {
+    m_image_ctx->image_lock.unlock_shared();
+    finish(-ENOENT);
+    return;
+  }
+
+  auto info = boost::get<cls::rbd::MirrorPrimarySnapshotNamespace>(
+    &snap_info->snap_namespace);
+  if (info == nullptr) {
+    lderr(cct) << "not mirror primary snapshot (snap_id=" << m_snap_id << ")"
+               << dendl;
+    m_image_ctx->image_lock.unlock_shared();
+    finish(-EINVAL);
+    return;
+  }
+
+  if (info->mirror_peer_uuids.count(m_mirror_peer_uuid) == 0 ||
+      info->mirror_peer_uuids.size() == 1U) {
+    m_image_ctx->image_lock.unlock_shared();
+    remove_snapshot();
+    return;
+  }
+
+  m_image_ctx->image_lock.unlock_shared();
+
+  ldout(cct, 20) << dendl;
+
+  librados::ObjectWriteOperation op;
+  librbd::cls_client::mirror_image_snapshot_unlink_peer(&op, m_snap_id,
+                                                        m_mirror_peer_uuid);
+  auto aio_comp = create_rados_callback<
+    UnlinkPeerRequest<I>, &UnlinkPeerRequest<I>::handle_unlink_peer>(this);
+  int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, aio_comp,
+                                          &op);
+  ceph_assert(r == 0);
+  aio_comp->release();
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::handle_unlink_peer(int r) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << "r=" << r << dendl;
+
+  if (r == -ERESTART || r == -ENOENT) {
+    refresh_image();
+    return;
+  }
+
+  if (r < 0) {
+    lderr(cct) << "failed to unlink peer: " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  notify_update();
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::notify_update() {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+  auto ctx = create_context_callback<
+    UnlinkPeerRequest<I>, &UnlinkPeerRequest<I>::handle_notify_update>(this);
+  m_image_ctx->notify_update(ctx);
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::handle_notify_update(int r) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << "r=" << r << dendl;
+
+  if (r < 0 && r != -ENOENT) {
+    lderr(cct) << "failed to notify update: " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  refresh_image();
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::remove_snapshot() {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+  cls::rbd::SnapshotNamespace snap_namespace;
+  std::string snap_name;
+  int r = 0;
+  {
+    std::shared_lock image_locker{m_image_ctx->image_lock};
+
+    auto snap_info = m_image_ctx->get_snap_info(m_snap_id);
+    if (!snap_info) {
+      r = -ENOENT;
+    } else {
+      snap_namespace = snap_info->snap_namespace;
+      snap_name = snap_info->name;
+    }
+  }
+
+  if (r == ENOENT) {
+    finish(0);
+    return;
+  }
+
+  auto info = boost::get<cls::rbd::MirrorPrimarySnapshotNamespace>(
+    &snap_namespace);
+  ceph_assert(info);
+
+  if (info->mirror_peer_uuids.size() > 1 ||
+      info->mirror_peer_uuids.count(m_mirror_peer_uuid) == 0) {
+    finish(0);
+    return;
+  }
+
+  auto ctx = create_context_callback<
+    UnlinkPeerRequest<I>, &UnlinkPeerRequest<I>::handle_remove_snapshot>(this);
+  m_image_ctx->operations->snap_remove(snap_namespace, snap_name, ctx);
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::handle_remove_snapshot(int r) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << "r=" << r << dendl;
+
+  if (r < 0 && r != -ENOENT) {
+    lderr(cct) << "failed to remove snapshot: " << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  finish(0);
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::finish(int r) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 20) << "r=" << r << dendl;
+
+  auto on_finish = m_on_finish;
+  delete this;
+  on_finish->complete(r);
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+template class librbd::mirror::snapshot::UnlinkPeerRequest<librbd::ImageCtx>;
diff --git a/src/librbd/mirror/snapshot/UnlinkPeerRequest.h b/src/librbd/mirror/snapshot/UnlinkPeerRequest.h
new file mode 100644 (file)
index 0000000..c42f892
--- /dev/null
@@ -0,0 +1,90 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_UNLINK_PEER_REQUEST_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_UNLINK_PEER_REQUEST_H
+
+#include "include/buffer.h"
+
+#include <string>
+#include <set>
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace mirror {
+namespace snapshot {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class UnlinkPeerRequest {
+public:
+  static UnlinkPeerRequest *create(ImageCtxT *image_ctx, uint64_t snap_id,
+                                   const std::string &mirror_peer_uuid,
+                                   Context *on_finish) {
+    return new UnlinkPeerRequest(image_ctx, snap_id, mirror_peer_uuid,
+                                 on_finish);
+  }
+
+  UnlinkPeerRequest(ImageCtxT *image_ctx, uint64_t snap_id,
+                    const std::string &mirror_peer_uuid, Context *on_finish)
+    : m_image_ctx(image_ctx), m_snap_id(snap_id),
+      m_mirror_peer_uuid(mirror_peer_uuid), m_on_finish(on_finish) {
+  }
+
+  void send();
+
+private:
+  /*
+   * @verbatim
+   *
+   * <start>
+   *    |
+   *    v
+   * REFRESH_IMAGE <--------------------------\
+   *    |                     ^ (not found    |
+   *    |                     *  or last)     |
+   *    |                     *               |
+   *    |\---------------> UNLINK_PEER --> NOTIFY_UPDATE
+   *    |   (peer not last)
+   *    |
+   *    |
+   *    |\---------------> REMOVE_SNAPSHOT
+   *    |   (peer last)       |
+   *    |                     |
+   *    |(peer not found)     |
+   *    v                     |
+   * <finish> <---------------/
+   *
+   * @endverbatim
+   */
+
+  ImageCtxT *m_image_ctx;
+  uint64_t m_snap_id;
+  std::string m_mirror_peer_uuid;
+  Context *m_on_finish;
+
+  void refresh_image();
+  void handle_refresh_image(int r);
+
+  void unlink_peer();
+  void handle_unlink_peer(int r);
+
+  void notify_update();
+  void handle_notify_update(int r);
+
+  void remove_snapshot();
+  void handle_remove_snapshot(int r);
+
+  void finish(int r);
+};
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+extern template class librbd::mirror::snapshot::UnlinkPeerRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_UNLINK_PEER_REQUEST_H
index 64e67f9485ff32bb1e298e384427f30ad0169d48..7f08342f97d73918ebefbe5b8d295e28dba32ff3 100644 (file)
@@ -85,6 +85,7 @@ set(unittest_librbd_srcs
   managed_lock/test_mock_GetLockerRequest.cc
   managed_lock/test_mock_ReacquireRequest.cc
   managed_lock/test_mock_ReleaseRequest.cc
+  mirror/snapshot/test_mock_UnlinkPeerRequest.cc
   mirror/test_mock_DisableRequest.cc
   object_map/test_mock_InvalidateRequest.cc
   object_map/test_mock_LockRequest.cc
diff --git a/src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc b/src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc
new file mode 100644 (file)
index 0000000..3c81aef
--- /dev/null
@@ -0,0 +1,344 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockOperations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+  explicit MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
+  }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+// template definitions
+#include "librbd/mirror/snapshot/UnlinkPeerRequest.cc"
+template class librbd::mirror::snapshot::UnlinkPeerRequest<librbd::MockTestImageCtx>;
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class TestMockMirrorSnapshotUnlinkPeerRequest : public TestMockFixture {
+public:
+  typedef UnlinkPeerRequest<MockTestImageCtx> MockUnlinkPeerRequest;
+
+  uint64_t m_snap_seq = 0;
+
+  uint64_t snap_create(MockTestImageCtx &mock_image_ctx,
+                   const cls::rbd::SnapshotNamespace &ns,
+                   const std::string& snap_name) {
+    EXPECT_TRUE(mock_image_ctx.snap_info.insert(
+                  {++m_snap_seq,
+                   SnapInfo{snap_name, ns, 0, {}, 0, 0, {}}}).second);
+    return m_snap_seq;
+  }
+
+  void expect_get_snap_info(MockTestImageCtx &mock_image_ctx,
+                            librados::snap_t snap_id) {
+    EXPECT_CALL(mock_image_ctx, get_snap_info(snap_id))
+      .WillRepeatedly(Invoke([&mock_image_ctx](
+                                 librados::snap_t snap_id) -> librbd::SnapInfo * {
+                               auto it = mock_image_ctx.snap_info.find(snap_id);
+                               if (it == mock_image_ctx.snap_info.end()) {
+                                 return nullptr;
+                               }
+                               return &it->second;
+                             }));
+  }
+
+  void expect_is_refresh_required(MockTestImageCtx &mock_image_ctx,
+                                  bool refresh_required) {
+    EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
+      .WillOnce(Return(refresh_required));
+  }
+
+  void expect_refresh_image(MockTestImageCtx &mock_image_ctx, int r) {
+    EXPECT_CALL(*mock_image_ctx.state, refresh(_))
+      .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_unlink_peer(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
+                          const std::string &peer_uuid, int r) {
+    using ceph::encode;
+    bufferlist bl;
+    encode(snapid_t{snap_id}, bl);
+    encode(peer_uuid, bl);
+
+    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+                     StrEq("mirror_image_snapshot_unlink_peer"),
+                     ContentsEqual(bl), _, _))
+      .WillOnce(Invoke([&mock_image_ctx, snap_id, peer_uuid, r](auto&&... args) -> int {
+                         if (r == 0) {
+                           auto it = mock_image_ctx.snap_info.find(snap_id);
+                           EXPECT_NE(it, mock_image_ctx.snap_info.end());
+                           auto info =
+                             boost::get<cls::rbd::MirrorPrimarySnapshotNamespace>(
+                               &it->second.snap_namespace);
+                           EXPECT_NE(nullptr, info);
+                           EXPECT_NE(0, info->mirror_peer_uuids.erase(
+                                       peer_uuid));
+                         }
+                         return r;
+                       }));
+  }
+
+  void expect_notify_update(MockTestImageCtx &mock_image_ctx, int r) {
+    EXPECT_CALL(mock_image_ctx, notify_update(_))
+      .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_remove_snapshot(MockTestImageCtx &mock_image_ctx,
+                              uint64_t snap_id, int r) {
+    EXPECT_CALL(*mock_image_ctx.operations, snap_remove(_, _, _))
+      .WillOnce(Invoke([&mock_image_ctx, snap_id, r](
+                           const cls::rbd::SnapshotNamespace &snap_namespace,
+                           const std::string &snap_name, Context *on_finish) {
+                         if (r == 0) {
+                           auto it = mock_image_ctx.snap_info.find(snap_id);
+                           EXPECT_NE(it, mock_image_ctx.snap_info.end());
+                           EXPECT_EQ(it->second.snap_namespace, snap_namespace);
+                           EXPECT_EQ(it->second.name, snap_name);
+                           mock_image_ctx.snap_info.erase(it);
+                         }
+                         mock_image_ctx.image_ctx->op_work_queue->queue(
+                           on_finish, r);
+                       }));
+  }
+};
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, Success) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorPrimarySnapshotNamespace ns{false, {"peer1_uuid", "peer2_uuid"}};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+  expect_unlink_peer(mock_image_ctx, snap_id, "peer1_uuid", 0);
+  expect_notify_update(mock_image_ctx, 0);
+  expect_refresh_image(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
+                                       &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshot) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorPrimarySnapshotNamespace ns{false, {"peer_uuid"}};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+  expect_remove_snapshot(mock_image_ctx, snap_id, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+                                       &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotDNE) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+
+  expect_get_snap_info(mock_image_ctx, 123);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid", &ctx);
+  req->send();
+  ASSERT_EQ(-ENOENT, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, PeerDNE) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorPrimarySnapshotNamespace ns{false, {"peer_uuid"}};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "unknown_peer",
+                                       &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, InvalidSnapshot) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::UserSnapshotNamespace ns;
+  auto snap_id = snap_create(mock_image_ctx, ns, "user_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, false);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+                                       &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RefreshError) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, true);
+  expect_refresh_image(mock_image_ctx, -EINVAL);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, 123, "peer_uuid", &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, UnlinkError) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorPrimarySnapshotNamespace ns{false,
+                                              {"peer1_uuid", "peer2_uuid"}};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, false);
+  expect_unlink_peer(mock_image_ctx, snap_id, "peer1_uuid", -EINVAL);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
+                                       &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, NotifyError) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorPrimarySnapshotNamespace ns{false,
+                                              {"peer1_uuid", "peer2_uuid"}};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, false);
+  expect_unlink_peer(mock_image_ctx, snap_id, "peer1_uuid", 0);
+  expect_notify_update(mock_image_ctx, -EINVAL);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer1_uuid",
+                                       &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshotError) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorPrimarySnapshotNamespace ns{false, {"peer_uuid"}};
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+
+  expect_get_snap_info(mock_image_ctx, snap_id);
+
+  InSequence seq;
+
+  expect_is_refresh_required(mock_image_ctx, false);
+  expect_remove_snapshot(mock_image_ctx, snap_id, -EINVAL);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+                                       &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
index e7948c6284601dffdbd66d4b26777ca0966bfc2b..4970f675821f9c04d82cf6f40b9ebdb4abcc61d8 100644 (file)
 #include "librbd/internal.h"
 #include "librbd/ObjectMap.h"
 #include "librbd/Operations.h"
+#include "librbd/api/Image.h"
 #include "librbd/io/AioCompletion.h"
 #include "librbd/io/ImageRequest.h"
 #include "librbd/io/ImageRequestWQ.h"
 #include "librbd/journal/Types.h"
-#include "librbd/api/Image.h"
 #include "journal/Journaler.h"
 #include "journal/Settings.h"
 #include "common/Cond.h"