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
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,
#include "include/buffer.h"
#include "cls/rbd/cls_rbd_client.h"
+#include "include/rados/librados.hpp"
#include <string>
#include <set>
* | 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 |
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);
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);
.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(_, _, _))
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();