#include "librbd/TrashWatcher.h"
#include "librbd/Utils.h"
#include "librbd/api/Image.h"
+#include "librbd/mirror/DisableRequest.h"
+#include "librbd/mirror/EnableRequest.h"
#include "librbd/trash/MoveRequest.h"
#define dout_subsys ceph_subsys_rbd
namespace librbd {
namespace api {
+namespace {
+
+template <typename I>
+int disable_mirroring(I *ictx) {
+ if (!ictx->test_features(RBD_FEATURE_JOURNALING)) {
+ return 0;
+ }
+
+ cls::rbd::MirrorImage mirror_image;
+ int r = cls_client::mirror_image_get(&ictx->md_ctx, ictx->id, &mirror_image);
+ if (r == -ENOENT) {
+ ldout(ictx->cct, 10) << "mirroring is not enabled for this image" << dendl;
+ return 0;
+ }
+
+ if (r < 0) {
+ lderr(ictx->cct) << "failed to retrieve mirror image: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ ldout(ictx->cct, 10) << dendl;
+
+ C_SaferCond ctx;
+ auto req = mirror::DisableRequest<I>::create(ictx, false, true, &ctx);
+ req->send();
+ r = ctx.wait();
+ if (r < 0) {
+ lderr(ictx->cct) << "failed to disable mirroring: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
+template <typename I>
+int enable_mirroring(IoCtx &io_ctx, const std::string &image_id) {
+ auto cct = reinterpret_cast<CephContext*>(io_ctx.cct());
+
+ uint64_t features;
+ uint64_t incompatible_features;
+ int r = cls_client::get_features(&io_ctx, util::header_name(image_id), true,
+ &features, &incompatible_features);
+ if (r < 0) {
+ lderr(cct) << "failed to retrieve features: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ if ((features & RBD_FEATURE_JOURNALING) == 0) {
+ return 0;
+ }
+
+ cls::rbd::MirrorMode mirror_mode;
+ r = cls_client::mirror_mode_get(&io_ctx, &mirror_mode);
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to retrieve mirror mode: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ if (mirror_mode != cls::rbd::MIRROR_MODE_POOL) {
+ ldout(cct, 10) << "not pool mirroring mode" << dendl;
+ return 0;
+ }
+
+ ldout(cct, 10) << dendl;
+
+ ThreadPool *thread_pool;
+ ContextWQ *op_work_queue;
+ ImageCtx::get_thread_pool_instance(cct, &thread_pool, &op_work_queue);
+ C_SaferCond ctx;
+ auto req = mirror::EnableRequest<I>::create(io_ctx, image_id, "",
+ op_work_queue, &ctx);
+ req->send();
+ r = ctx.wait();
+ if (r < 0) {
+ lderr(cct) << "failed to enable mirroring: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
+} // anonymous namespace
+
template <typename I>
int Trash<I>::move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
const std::string &image_name, uint64_t delay) {
return -EINVAL;
}
+ r = disable_mirroring<I>(ictx);
+ if (r < 0) {
+ return r;
+ }
+
utime_t delete_time{ceph_clock_now()};
utime_t deferment_end_time{delete_time};
deferment_end_time += delay;
}
}
- ldout(cct, 2) << "adding rbd image from v2 directory..." << dendl;
+ ldout(cct, 2) << "adding rbd image to v2 directory..." << dendl;
r = cls_client::dir_add_image(&io_ctx, RBD_DIRECTORY, image_name,
image_id);
if (r < 0 && r != -EEXIST) {
return r;
}
+ r = enable_mirroring<I>(io_ctx, image_id);
+ if (r < 0) {
+ // not fatal -- ignore
+ }
+
ldout(cct, 2) << "removing image from trash..." << dendl;
r = cls_client::trash_remove(&io_ctx, image_id);
if (r < 0 && r != -ENOENT) {
ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
}
+ void check_trash_move_restore(rbd_mirror_mode_t mirror_mode,
+ bool enable_mirroring) {
+
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
+
+ int order = 20;
+ uint64_t features = RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+
+ if (enable_mirroring) {
+ ASSERT_EQ(0, image.mirror_image_enable());
+ }
+
+ std::string image_id;
+ ASSERT_EQ(0, image.get_id(&image_id));
+
+ ASSERT_EQ(0, m_rbd.trash_move(m_ioctx, image_name.c_str(), 100));
+
+ librbd::mirror_image_info_t mirror_image;
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ ASSERT_EQ(mirror_image.state, RBD_MIRROR_IMAGE_DISABLED);
+
+ ASSERT_EQ(0, m_rbd.trash_restore(m_ioctx, image_id.c_str(), ""));
+
+ ASSERT_EQ(0, image.mirror_image_get_info(&mirror_image, sizeof(mirror_image)));
+ if (mirror_mode == RBD_MIRROR_MODE_POOL) {
+ ASSERT_EQ(mirror_image.state, RBD_MIRROR_IMAGE_ENABLED);
+ } else {
+ ASSERT_EQ(mirror_image.state, RBD_MIRROR_IMAGE_DISABLED);
+ }
+
+ image.close();
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
+ }
+
void setup_mirror_peer(librados::IoCtx &io_ctx, librbd::Image &image) {
ASSERT_EQ(0, image.snap_create("sync-point-snap"));
true, true);
}
+TEST_F(TestMirroring, TrashMoveRestore_PoolMode) {
+ check_trash_move_restore(RBD_MIRROR_MODE_POOL, false);
+}
+
+TEST_F(TestMirroring, TrashMoveRestore_ImageMode_MirroringDisabled) {
+ check_trash_move_restore(RBD_MIRROR_MODE_IMAGE, false);
+}
+
+TEST_F(TestMirroring, TrashMoveRestore_ImageMode_MirroringEnabled) {
+ check_trash_move_restore(RBD_MIRROR_MODE_IMAGE, true);
+}
+
TEST_F(TestMirroring, MirrorStatusList) {
std::vector<uint64_t>
features_vec(5, RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING);