]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: unlink group snapshot when removing mirror snapshot
authorMykola Golub <mgolub@suse.com>
Fri, 19 Feb 2021 17:57:06 +0000 (17:57 +0000)
committerPrasanna Kumar Kalever <prasanna.kalever@redhat.com>
Thu, 24 Apr 2025 15:56:22 +0000 (21:26 +0530)
Signed-off-by: Mykola Golub <mgolub@suse.com>
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
src/librbd/mirror/snapshot/UnlinkPeerRequest.cc
src/librbd/mirror/snapshot/UnlinkPeerRequest.h
src/test/librbd/mirror/snapshot/test_mock_UnlinkPeerRequest.cc

index 35313f6277981bb6876724dcc3b316043aea2d91..dc596e5175ce9adfddb095a8792c425517676ec7 100644 (file)
@@ -103,7 +103,7 @@ void UnlinkPeerRequest<I>::unlink_peer() {
       have_newer_mirror_snapshot) {
     if (m_allow_remove) {
       m_image_ctx->image_lock.unlock_shared();
-      remove_snapshot(snap_namespace, snap_name);
+      unlink_group_snapshot(snap_namespace, snap_name);
       return;
     } else {
       ldout(cct, 15) << "skipping removal of snapshot: snap_id=" << m_snap_id
@@ -187,6 +187,57 @@ void UnlinkPeerRequest<I>::handle_notify_update(int r) {
   refresh_image();
 }
 
+template <typename I>
+void UnlinkPeerRequest<I>::unlink_group_snapshot(
+    const cls::rbd::SnapshotNamespace& snap_namespace,
+    const std::string& snap_name) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 15) << dendl;
+
+  auto info = std::get_if<cls::rbd::MirrorSnapshotNamespace>(&snap_namespace);
+  if (!info->group_spec.is_valid()) {
+    remove_snapshot(snap_namespace, snap_name);
+    return;
+  }
+
+  int r = util::create_ioctx(m_image_ctx->md_ctx, "group", info->group_spec.pool_id,
+      {}, &m_group_io_ctx);
+  if (r < 0) {
+    remove_snapshot(snap_namespace, snap_name);
+    return;
+  }
+
+  librados::ObjectWriteOperation op;
+  cls::rbd::ImageSnapshotSpec image_snap = {m_image_ctx->md_ctx.get_id(),
+                                            m_image_ctx->id, m_snap_id};
+  librbd::cls_client::group_snap_unlink(&op, info->group_snap_id, image_snap);
+  auto ctx = new LambdaContext([this, snap_namespace, snap_name](int r) {
+      handle_unlink_group_snapshot(snap_namespace, snap_name, r);
+    });
+  auto aio_comp = create_rados_callback(ctx);
+  r = m_group_io_ctx.aio_operate(
+      util::group_header_name(info->group_spec.group_id), aio_comp, &op);
+  ceph_assert(r == 0);
+  aio_comp->release();
+}
+
+template <typename I>
+void UnlinkPeerRequest<I>::handle_unlink_group_snapshot(
+    const cls::rbd::SnapshotNamespace& snap_namespace,
+    const std::string& snap_name, int r) {
+  CephContext *cct = m_image_ctx->cct;
+  ldout(cct, 15) << "r=" << r << dendl;
+
+  if (r < 0 && r != -ENOENT) {
+    lderr(cct) << "failed to unlink group snapshot: " << cpp_strerror(r)
+               << dendl;
+    finish(r);
+    return;
+  }
+
+  remove_snapshot(snap_namespace, snap_name);
+}
+
 template <typename I>
 void UnlinkPeerRequest<I>::remove_snapshot(
     const cls::rbd::SnapshotNamespace& snap_namespace,
index 192b40d6e5e676a6357e365695868f5b3fb386fc..8690efadc30200702defa9747d3f0aa11a120908 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "include/buffer.h"
 #include "cls/rbd/cls_rbd_client.h"
+#include "include/rados/librados.hpp"
 
 #include <string>
 #include <set>
@@ -55,10 +56,10 @@ private:
    *    |    no newer mirror
    *    |    snap exists)
    *    |
-   *    |\---------------> REMOVE_SNAPSHOT
-   *    |   (last peer and    |
-   *    |    newer mirror     |
-   *    |    snap exists)     |
+   *    |\---------------> UNLINK_GROUP_SNAPSHOT
+   *    |   (last peer and    |     (skip if not group snapshot)
+   *    |    newer mirror     v
+   *    |    snap exists)  REMOVE_SNAPSHOT
    *    |                     |
    *    |(peer not found)     |
    *    v                     |
@@ -72,6 +73,7 @@ private:
   std::string m_mirror_peer_uuid;
   bool m_allow_remove;
   Context *m_on_finish;
+  librados::IoCtx m_group_io_ctx;
 
   void refresh_image();
   void handle_refresh_image(int r);
@@ -82,6 +84,12 @@ private:
   void notify_update();
   void handle_notify_update(int r);
 
+  void unlink_group_snapshot(const cls::rbd::SnapshotNamespace& snap_namespace,
+                             const std::string& snap_name);
+  void handle_unlink_group_snapshot(
+      const cls::rbd::SnapshotNamespace& snap_namespace,
+      const std::string& snap_name, int r);
+
   void remove_snapshot(const cls::rbd::SnapshotNamespace& snap_namespace,
                        const std::string& snap_name);
   void handle_remove_snapshot(int r);
index 869bdecff479f6fb32ed3b1ee296abf1ac83d57e..c8400d0628a998dd20f157e675403c3e01ab76bd 100644 (file)
@@ -106,6 +106,34 @@ public:
       .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
   }
 
+  void expect_create_ioctx(MockTestImageCtx &mock_image_ctx,
+                           librados::MockTestMemIoCtxImpl **io_ctx_impl) {
+    *io_ctx_impl = &get_mock_io_ctx(mock_image_ctx.md_ctx);
+    auto rados_client = (*io_ctx_impl)->get_mock_rados_client();
+
+    EXPECT_CALL(*rados_client, create_ioctx(_, _))
+      .WillOnce(DoAll(GetReference(*io_ctx_impl), Return(*io_ctx_impl)));
+  }
+
+  void expect_unlink_group_snapshot(MockTestImageCtx &mock_image_ctx,
+                                    const std::string &group_id,
+                                    const std::string &group_snap_id,
+                                    const cls::rbd::ImageSnapshotSpec &image_snap,
+                                    int r) {
+    using ceph::encode;
+    bufferlist bl;
+    encode(group_snap_id, bl);
+    encode(image_snap, bl);
+
+    librados::MockTestMemIoCtxImpl *mock_io_ctx_impl;
+    expect_create_ioctx(mock_image_ctx, &mock_io_ctx_impl);
+
+    EXPECT_CALL(*mock_io_ctx_impl,
+                exec(util::group_header_name(group_id), _, StrEq("rbd"),
+                     StrEq("group_snap_unlink"), ContentsEqual(bl), _, _, _))
+      .WillOnce(Return(r));
+  }
+
   void expect_remove_snapshot(MockTestImageCtx &mock_image_ctx,
                               uint64_t snap_id, int r) {
     EXPECT_CALL(*mock_image_ctx.operations, snap_remove(_, _, _))
@@ -212,6 +240,39 @@ TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, RemoveSnapshotNotAllowed) {
   ASSERT_EQ(0, ctx.wait());
 }
 
+TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, UnlinkGroupSnapshot) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+  cls::rbd::MirrorSnapshotNamespace ns{
+    cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"peer_uuid"},
+    "", CEPH_NOSNAP};
+  ns.group_spec = {"group_id", mock_image_ctx.md_ctx.get_id()};
+  ns.group_snap_id = "group_snap_id";
+  auto snap_id = snap_create(mock_image_ctx, ns, "mirror_snap");
+  ns.mirror_peer_uuids = {"peer_uuid"};
+  snap_create(mock_image_ctx, ns, "mirror_snap2");
+
+  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_group_snapshot(mock_image_ctx, "group_id", "group_snap_id",
+                               {ictx->md_ctx.get_id(), ictx->id, snap_id}, 0);
+  expect_remove_snapshot(mock_image_ctx, snap_id, 0);
+
+  C_SaferCond ctx;
+  auto req = new MockUnlinkPeerRequest(&mock_image_ctx, snap_id, "peer_uuid",
+                                       true, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
 TEST_F(TestMockMirrorSnapshotUnlinkPeerRequest, SnapshotRemoveEmptyPeers) {
   REQUIRE_FORMAT_V2();