1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2016 SUSE LINUX GmbH
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
14 #include "include/rados/librados.hpp"
15 #include "include/rbd/librbd.hpp"
16 #include "include/stringify.h"
17 #include "cls/rbd/cls_rbd_types.h"
18 #include "cls/rbd/cls_rbd_client.h"
19 #include "tools/rbd_mirror/ImageDeleter.h"
20 #include "tools/rbd_mirror/ServiceDaemon.h"
21 #include "tools/rbd_mirror/Threads.h"
22 #include "tools/rbd_mirror/Types.h"
23 #include "librbd/ImageCtx.h"
24 #include "librbd/ImageState.h"
25 #include "librbd/Operations.h"
26 #include "librbd/Journal.h"
27 #include "librbd/internal.h"
28 #include "librbd/Utils.h"
29 #include "librbd/api/Image.h"
30 #include "librbd/api/Mirror.h"
31 #include "librbd/journal/DisabledPolicy.h"
32 #include "test/rbd_mirror/test_fixture.h"
34 #include "test/librados/test.h"
35 #include "gtest/gtest.h"
37 #define GLOBAL_IMAGE_ID "global_image_id"
38 #define GLOBAL_CLONE_IMAGE_ID "global_image_id_clone"
40 #define dout_subsys ceph_subsys_rbd_mirror
42 using rbd::mirror::RadosRef
;
43 using rbd::mirror::TestFixture
;
44 using namespace librbd
;
45 using cls::rbd::MirrorImageState
;
48 void register_test_rbd_mirror_image_deleter() {
51 class TestImageDeleter
: public TestFixture
{
53 const std::string m_local_mirror_uuid
= "local mirror uuid";
54 const std::string m_remote_mirror_uuid
= "remote mirror uuid";
56 void SetUp() override
{
59 m_service_daemon
.reset(new rbd::mirror::ServiceDaemon
<>(g_ceph_context
,
62 librbd::api::Mirror
<>::mode_set(m_local_io_ctx
, RBD_MIRROR_MODE_IMAGE
);
64 m_deleter
= new rbd::mirror::ImageDeleter
<>(m_local_io_ctx
, m_threads
,
65 m_service_daemon
.get());
67 m_local_image_id
= librbd::util::generate_image_id(m_local_io_ctx
);
68 librbd::ImageOptions image_opts
;
69 image_opts
.set(RBD_IMAGE_OPTION_FEATURES
,
70 (RBD_FEATURES_ALL
& ~RBD_FEATURES_IMPLICIT_ENABLE
));
71 EXPECT_EQ(0, librbd::create(m_local_io_ctx
, m_image_name
, m_local_image_id
,
72 1 << 20, image_opts
, GLOBAL_IMAGE_ID
,
73 m_remote_mirror_uuid
, true));
75 cls::rbd::MirrorImage
mirror_image(
76 GLOBAL_IMAGE_ID
, MirrorImageState::MIRROR_IMAGE_STATE_ENABLED
);
77 EXPECT_EQ(0, cls_client::mirror_image_set(&m_local_io_ctx
, m_local_image_id
,
81 void TearDown() override
{
85 m_deleter
->shut_down(&ctx
);
89 m_service_daemon
.reset();
91 TestFixture::TearDown();
94 void init_image_deleter() {
96 m_deleter
->init(&ctx
);
97 ASSERT_EQ(0, ctx
.wait());
100 void remove_image() {
101 cls::rbd::MirrorImage mirror_image
;
102 int r
= cls_client::mirror_image_get(&m_local_io_ctx
, m_local_image_id
,
104 EXPECT_EQ(1, r
== 0 || r
== -ENOENT
);
106 mirror_image
.state
= MirrorImageState::MIRROR_IMAGE_STATE_ENABLED
;
107 EXPECT_EQ(0, cls_client::mirror_image_set(&m_local_io_ctx
,
113 NoOpProgressContext ctx
;
114 r
= librbd::api::Image
<>::remove(m_local_io_ctx
, m_image_name
, ctx
);
115 EXPECT_EQ(1, r
== 0 || r
== -ENOENT
);
118 void promote_image(ImageCtx
*ictx
=nullptr) {
122 ictx
= new ImageCtx("", m_local_image_id
, "", m_local_io_ctx
,
124 r
= ictx
->state
->open(0);
128 EXPECT_EQ(1, r
== 0 || r
== -ENOENT
);
131 int r2
= librbd::api::Mirror
<>::image_promote(ictx
, true);
132 EXPECT_EQ(1, r2
== 0 || r2
== -EINVAL
);
136 EXPECT_EQ(0, ictx
->state
->close());
140 void demote_image(ImageCtx
*ictx
=nullptr) {
143 ictx
= new ImageCtx("", m_local_image_id
, "", m_local_io_ctx
,
145 EXPECT_EQ(0, ictx
->state
->open(0));
149 EXPECT_EQ(0, librbd::api::Mirror
<>::image_demote(ictx
));
152 EXPECT_EQ(0, ictx
->state
->close());
156 void create_snapshot(std::string snap_name
="snap1", bool protect
=false) {
157 ImageCtx
*ictx
= new ImageCtx("", m_local_image_id
, "", m_local_io_ctx
,
159 EXPECT_EQ(0, ictx
->state
->open(0));
161 RWLock::WLocker
snap_locker(ictx
->snap_lock
);
162 ictx
->set_journal_policy(new librbd::journal::DisabledPolicy());
165 EXPECT_EQ(0, ictx
->operations
->snap_create(
166 cls::rbd::UserSnapshotNamespace(), snap_name
.c_str()));
169 EXPECT_EQ(0, ictx
->operations
->snap_protect(
170 cls::rbd::UserSnapshotNamespace(), snap_name
.c_str()));
173 EXPECT_EQ(0, ictx
->state
->close());
176 std::string
create_clone() {
177 ImageCtx
*ictx
= new ImageCtx("", m_local_image_id
, "", m_local_io_ctx
,
179 EXPECT_EQ(0, ictx
->state
->open(0));
181 RWLock::WLocker
snap_locker(ictx
->snap_lock
);
182 ictx
->set_journal_policy(new librbd::journal::DisabledPolicy());
185 EXPECT_EQ(0, ictx
->operations
->snap_create(
186 cls::rbd::UserSnapshotNamespace(), "snap1"));
187 EXPECT_EQ(0, ictx
->operations
->snap_protect(
188 cls::rbd::UserSnapshotNamespace(), "snap1"));
189 EXPECT_EQ(0, librbd::api::Image
<>::snap_set(
190 ictx
, cls::rbd::UserSnapshotNamespace(), "snap1"));
192 std::string clone_id
= librbd::util::generate_image_id(m_local_io_ctx
);
193 librbd::ImageOptions clone_opts
;
194 clone_opts
.set(RBD_IMAGE_OPTION_FEATURES
, ictx
->features
);
195 EXPECT_EQ(0, librbd::clone(m_local_io_ctx
, m_local_image_id
.c_str(),
196 nullptr, "snap1", m_local_io_ctx
,
197 clone_id
.c_str(), "clone1", clone_opts
,
198 GLOBAL_CLONE_IMAGE_ID
, m_remote_mirror_uuid
));
200 cls::rbd::MirrorImage
mirror_image(
201 GLOBAL_CLONE_IMAGE_ID
, MirrorImageState::MIRROR_IMAGE_STATE_ENABLED
);
202 EXPECT_EQ(0, cls_client::mirror_image_set(&m_local_io_ctx
, clone_id
,
204 EXPECT_EQ(0, ictx
->state
->close());
208 void check_image_deleted() {
209 ImageCtx
*ictx
= new ImageCtx("", m_local_image_id
, "", m_local_io_ctx
,
211 EXPECT_EQ(-ENOENT
, ictx
->state
->open(0));
213 cls::rbd::MirrorImage mirror_image
;
214 EXPECT_EQ(-ENOENT
, cls_client::mirror_image_get(&m_local_io_ctx
,
219 int trash_move(const std::string
& global_image_id
) {
221 rbd::mirror::ImageDeleter
<>::trash_move(m_local_io_ctx
, global_image_id
,
222 true, m_threads
->work_queue
, &ctx
);
227 std::string m_local_image_id
;
228 std::unique_ptr
<rbd::mirror::ServiceDaemon
<>> m_service_daemon
;
229 rbd::mirror::ImageDeleter
<> *m_deleter
;
232 TEST_F(TestImageDeleter
, ExistingTrashMove
) {
233 ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID
));
236 m_deleter
->wait_for_deletion(m_local_image_id
, false, &ctx
);
237 init_image_deleter();
239 ASSERT_EQ(0, ctx
.wait());
242 TEST_F(TestImageDeleter
, LiveTrashMove
) {
243 init_image_deleter();
246 m_deleter
->wait_for_deletion(m_local_image_id
, false, &ctx
);
248 ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID
));
249 ASSERT_EQ(0, ctx
.wait());
252 TEST_F(TestImageDeleter
, Delete_Image_With_Snapshots
) {
253 init_image_deleter();
254 create_snapshot("snap1");
255 create_snapshot("snap2");
258 m_deleter
->wait_for_deletion(m_local_image_id
, false, &ctx
);
259 ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID
));
260 EXPECT_EQ(0, ctx
.wait());
262 ASSERT_EQ(0u, m_deleter
->get_delete_queue_items().size());
263 ASSERT_EQ(0u, m_deleter
->get_failed_queue_items().size());
266 TEST_F(TestImageDeleter
, Delete_Image_With_ProtectedSnapshots
) {
267 init_image_deleter();
268 create_snapshot("snap1", true);
269 create_snapshot("snap2", true);
272 m_deleter
->wait_for_deletion(m_local_image_id
, false, &ctx
);
273 ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID
));
274 EXPECT_EQ(0, ctx
.wait());
276 ASSERT_EQ(0u, m_deleter
->get_delete_queue_items().size());
277 ASSERT_EQ(0u, m_deleter
->get_failed_queue_items().size());
280 TEST_F(TestImageDeleter
, Delete_Image_With_Clone
) {
281 init_image_deleter();
282 std::string clone_id
= create_clone();
285 m_deleter
->set_busy_timer_interval(0.1);
286 m_deleter
->wait_for_deletion(m_local_image_id
, false, &ctx1
);
287 ASSERT_EQ(0, trash_move(GLOBAL_IMAGE_ID
));
288 EXPECT_EQ(-EBUSY
, ctx1
.wait());
291 m_deleter
->wait_for_deletion(clone_id
, false, &ctx2
);
292 ASSERT_EQ(0, trash_move(GLOBAL_CLONE_IMAGE_ID
));
293 EXPECT_EQ(0, ctx2
.wait());
296 m_deleter
->wait_for_deletion(m_local_image_id
, true, &ctx3
);
297 EXPECT_EQ(0, ctx3
.wait());
299 ASSERT_EQ(0u, m_deleter
->get_delete_queue_items().size());
300 ASSERT_EQ(0u, m_deleter
->get_failed_queue_items().size());