set_pool_mirror_mode ${CLUSTER2} ${POOL} 'pool'
for i in ${image2} ${image4}; do
- enable_journaling ${CLUSTER2} ${POOL} ${i}
wait_for_image_present ${CLUSTER1} ${POOL} ${i} 'present'
wait_for_snap_present ${CLUSTER1} ${POOL} ${i} 'snap2'
wait_for_image_replay_started ${CLUSTER1} ${POOL} ${i}
start_mirrors ${CLUSTER1}
wait_for_image_present ${CLUSTER1} ${POOL} ${image} 'deleted'
set_pool_mirror_mode ${CLUSTER2} ${POOL} 'pool'
-enable_journaling ${CLUSTER2} ${POOL} ${image}
wait_for_image_present ${CLUSTER1} ${POOL} ${image} 'present'
wait_for_image_replay_started ${CLUSTER1} ${POOL} ${image}
int Migration<I>::disable_mirroring(I *image_ctx, bool *was_enabled) {
*was_enabled = false;
- if (!image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
- return 0;
- }
-
cls::rbd::MirrorImage mirror_image;
int r = cls_client::mirror_image_get(&image_ctx->md_ctx, image_ctx->id,
&mirror_image);
template <typename I>
int Migration<I>::enable_mirroring(I *image_ctx, bool was_enabled) {
-
- if (!image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
- return 0;
- }
-
cls::rbd::MirrorMode mirror_mode;
int r = cls_client::mirror_mode_get(&image_ctx->md_ctx, &mirror_mode);
if (r < 0 && r != -ENOENT) {
}
void finish(int r) override {
- if (r < 0) {
+ if (r < 0 && r != -ENOENT) {
on_finish->complete(r);
return;
}
}
void finish(int r) override {
- if (r < 0) {
+ if (r < 0 && r != -ENOENT) {
on_finish->complete(r);
return;
}
std::shared_lock image_locker{ictx->image_lock};
ImageCtx *parent = ictx->parent;
if (parent) {
- if (relax_same_pool_parent_check &&
- parent->md_ctx.get_id() == ictx->md_ctx.get_id()) {
- if (!parent->test_features(RBD_FEATURE_JOURNALING)) {
- lderr(cct) << "journaling is not enabled for the parent" << dendl;
- return -EINVAL;
- }
- } else {
+ if (parent->md_ctx.get_id() != ictx->md_ctx.get_id() ||
+ !relax_same_pool_parent_check) {
cls::rbd::MirrorImage mirror_image_internal;
r = cls_client::mirror_image_get(&(parent->md_ctx), parent->id,
&mirror_image_internal);
}
}
- if (!ictx->test_features(RBD_FEATURE_JOURNALING)) {
- uint64_t features = RBD_FEATURE_JOURNALING;
- if (!ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
- features |= RBD_FEATURE_EXCLUSIVE_LOCK;
- }
- r = ictx->operations->update_features(features, true);
- if (r < 0) {
- lderr(cct) << "cannot enable journaling: " << cpp_strerror(r) << dendl;
- return r;
- }
- }
-
C_SaferCond ctx;
auto req = mirror::EnableRequest<ImageCtx>::create(ictx, &ctx);
req->send();
rollback = true;
return r;
}
-
- r = ictx->operations->update_features(RBD_FEATURE_JOURNALING, false);
- if (r < 0) {
- lderr(cct) << "cannot disable journaling: " << cpp_strerror(r) << dendl;
- // not fatal
- }
}
return 0;
return r;
}
+ // Enable only journal based mirroring
+
if ((features & RBD_FEATURE_JOURNALING) != 0) {
I *img_ctx = I::create("", img_pair.second, nullptr, io_ctx, false);
r = img_ctx->state->open(0);
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) {
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) {
snap_info.snap_namespace);
switch (snap_namespace_type) {
case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH:
+ case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY:
+ case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY:
return true;
default:
return false;
#include "librbd/Journal.h"
#include "librbd/Utils.h"
#include "librbd/mirror/GetInfoRequest.h"
+#include "librbd/mirror/snapshot/DemoteRequest.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << "r=" << r << dendl;
- if (r < 0) {
+ if (r < 0 && r != -ENOENT) {
lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r)
<< dendl;
finish(r);
m_image_ctx.owner_lock.lock_shared();
if (m_image_ctx.exclusive_lock == nullptr) {
m_image_ctx.owner_lock.unlock_shared();
- lderr(cct) << "exclusive lock is not active" << dendl;
- finish(-EINVAL);
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ lderr(cct) << "exclusive lock is not active" << dendl;
+ finish(-EINVAL);
+ } else {
+ demote();
+ }
return;
}
auto ctx = create_context_callback<
DemoteRequest<I>, &DemoteRequest<I>::handle_demote>(this);
- Journal<I>::demote(&m_image_ctx, ctx);
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ Journal<I>::demote(&m_image_ctx, ctx);
+ } else if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ auto req = mirror::snapshot::DemoteRequest<I>::create(&m_image_ctx, ctx);
+ req->send();
+ } else {
+ lderr(cct) << "unknown image mirror mode: " << m_mirror_image.mode << dendl;
+ m_ret_val = -EOPNOTSUPP;
+ release_lock();
+ }
}
template <typename I>
#include "librbd/Operations.h"
#include "librbd/Utils.h"
#include "librbd/journal/PromoteRequest.h"
+#include "librbd/mirror/GetInfoRequest.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
template <typename I>
void DisableRequest<I>::send() {
- send_get_mirror_image();
+ send_get_mirror_info();
}
template <typename I>
-void DisableRequest<I>::send_get_mirror_image() {
+void DisableRequest<I>::send_get_mirror_info() {
CephContext *cct = m_image_ctx->cct;
ldout(cct, 10) << this << " " << __func__ << dendl;
- librados::ObjectReadOperation op;
- cls_client::mirror_image_get_start(&op, m_image_ctx->id);
using klass = DisableRequest<I>;
- librados::AioCompletion *comp =
- create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
- m_out_bl.clear();
- int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
- ceph_assert(r == 0);
- comp->release();
+ Context *ctx = util::create_context_callback<
+ klass, &klass::handle_get_mirror_info>(this);
+
+ auto req = GetInfoRequest<I>::create(*m_image_ctx, &m_mirror_image,
+ &m_promotion_state, ctx);
+ req->send();
}
template <typename I>
-Context *DisableRequest<I>::handle_get_mirror_image(int *result) {
+Context *DisableRequest<I>::handle_get_mirror_info(int *result) {
CephContext *cct = m_image_ctx->cct;
ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
- if (*result == 0) {
- auto iter = m_out_bl.cbegin();
- *result = cls_client::mirror_image_get_finish(&iter, &m_mirror_image);
- }
-
if (*result < 0) {
if (*result == -ENOENT) {
ldout(cct, 20) << this << " " << __func__
<< ": mirroring is not enabled for this image" << dendl;
*result = 0;
- } else if (*result == -EOPNOTSUPP) {
- ldout(cct, 5) << this << " " << __func__
- << ": mirroring is not supported by OSD" << dendl;
} else {
- lderr(cct) << "failed to retrieve mirror image: " << cpp_strerror(*result)
+ lderr(cct) << "failed to get mirroring info: " << cpp_strerror(*result)
<< dendl;
}
return m_on_finish;
}
- send_get_tag_owner();
- return nullptr;
-}
-
-template <typename I>
-void DisableRequest<I>::send_get_tag_owner() {
- CephContext *cct = m_image_ctx->cct;
- ldout(cct, 10) << this << " " << __func__ << dendl;
-
- using klass = DisableRequest<I>;
- Context *ctx = util::create_context_callback<
- klass, &klass::handle_get_tag_owner>(this);
-
- Journal<I>::is_tag_owner(m_image_ctx, &m_is_primary, ctx);
-}
-
-template <typename I>
-Context *DisableRequest<I>::handle_get_tag_owner(int *result) {
- CephContext *cct = m_image_ctx->cct;
- ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
-
- if (*result < 0) {
- lderr(cct) << "failed to check tag ownership: " << cpp_strerror(*result)
- << dendl;
- return m_on_finish;
- }
+ m_is_primary = (m_promotion_state == PROMOTION_STATE_PRIMARY);
if (!m_is_primary && !m_force) {
lderr(cct) << "mirrored image is not primary, "
using klass = DisableRequest<I>;
librados::AioCompletion *comp =
create_rados_callback<klass, &klass::handle_set_mirror_image>(this);
- m_out_bl.clear();
int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op);
ceph_assert(r == 0);
comp->release();
*result = 0;
}
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ // remove mirroring snapshots
+
+ bool removing_snapshots = false;
+ {
+ std::lock_guard locker{m_lock};
+ std::shared_lock image_locker{m_image_ctx->image_lock};
+
+ for (auto &it : m_image_ctx->snap_info) {
+ auto &snap_info = it.second;
+ auto type = cls::rbd::get_snap_namespace_type(
+ snap_info.snap_namespace);
+ if (type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY ||
+ type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY) {
+ send_remove_snap("", snap_info.snap_namespace, snap_info.name);
+ removing_snapshots = true;
+ }
+ }
+ }
+
+ if (!removing_snapshots) {
+ send_remove_mirror_image();
+ }
+
+ return nullptr;
+ }
+
send_promote_image();
return nullptr;
}
m_current_ops[client_id]--;
if (*result < 0 && *result != -ENOENT) {
- lderr(cct) <<
- "failed to remove temporary snapshot created by remote peer: "
+ lderr(cct) << "failed to remove mirroring snapshot: "
<< cpp_strerror(*result) << dendl;
m_ret[client_id] = *result;
}
if (m_current_ops[client_id] == 0) {
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ ceph_assert(client_id.empty());
+ m_current_ops.erase(client_id);
+ if (m_ret[client_id] < 0) {
+ return m_on_finish;
+ }
+ send_remove_mirror_image();
+ return nullptr;
+ }
+
send_unregister_client(client_id);
}
using klass = DisableRequest<I>;
librados::AioCompletion *comp =
create_rados_callback<klass, &klass::handle_remove_mirror_image>(this);
- m_out_bl.clear();
int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op);
ceph_assert(r == 0);
comp->release();
#include "common/ceph_mutex.h"
#include "cls/journal/cls_journal_types.h"
#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/mirror/Types.h"
+
#include <map>
#include <string>
* <start>
* |
* v
- * GET_MIRROR_IMAGE * * * * * * * * * * * * * * * * * * * * * * *
- * | *
- * v *
- * GET_TAG_OWNER * * * * * * * * * * * * * * * * * * * * * * * *
+ * GET_MIRROR_INFO * * * * * * * * * * * * * * * * * * * * * * *
* | *
* v *
* SET_MIRROR_IMAGE * * * * * * * * * * * * * * * * * * * * * * *
Context *m_on_finish;
bool m_is_primary = false;
- bufferlist m_out_bl;
cls::rbd::MirrorImage m_mirror_image;
+ PromotionState m_promotion_state = PROMOTION_STATE_NON_PRIMARY;
std::set<cls::journal::Client> m_clients;
std::map<std::string, int> m_ret;
std::map<std::string, int> m_current_ops;
mutable ceph::mutex m_lock =
ceph::make_mutex("mirror::DisableRequest::m_lock");
- void send_get_mirror_image();
- Context *handle_get_mirror_image(int *result);
-
- void send_get_tag_owner();
- Context *handle_get_tag_owner(int *result);
+ void send_get_mirror_info();
+ Context *handle_get_mirror_info(int *result);
void send_set_mirror_image();
Context *handle_set_mirror_image(int *result);
}
*result = 0;
- m_mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
if (m_non_primary_global_image_id.empty()) {
uuid_d uuid_gen;
uuid_gen.generate_random();
m_mirror_image.global_image_id = m_non_primary_global_image_id;
}
+ send_get_features();
+ return nullptr;
+}
+
+template <typename I>
+void EnableRequest<I>::send_get_features() {
+ ldout(m_cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::get_features_start(&op, true);
+
+ using klass = EnableRequest<I>;
+ librados::AioCompletion *comp =
+ create_rados_callback<klass, &klass::handle_get_features>(this);
+ m_out_bl.clear();
+ int r = m_io_ctx.aio_operate(util::header_name(m_image_id), comp, &op,
+ &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *EnableRequest<I>::handle_get_features(int *result) {
+ ldout(m_cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ uint64_t features, incompatible_features;
+ if (*result == 0) {
+ auto iter = m_out_bl.cbegin();
+ *result = cls_client::get_features_finish(&iter, &features,
+ &incompatible_features);
+ }
+
+ if (*result != 0) {
+ lderr(m_cct) << "failed to retrieve image features: "
+ << cpp_strerror(*result) << dendl;
+ return m_on_finish;
+ }
+
+ // TODO: be explicit about the image mirror mode
+
+ m_mirror_image.mode = (features & RBD_FEATURE_JOURNALING) != 0 ?
+ cls::rbd::MIRROR_IMAGE_MODE_JOURNAL : cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT;
+ m_mirror_image.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
+
send_get_tag_owner();
return nullptr;
}
template <typename I>
void EnableRequest<I>::send_get_tag_owner() {
- if (!m_non_primary_global_image_id.empty()) {
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT ||
+ !m_non_primary_global_image_id.empty()) {
send_set_mirror_image();
return;
}
* GET_MIRROR_IMAGE * * * * * * *
* | * (on error)
* v *
- * GET_TAG_OWNER * * * * * * * *
+ * GET_FEATURES * * * * * * * * *
* | *
* v *
+ * GET_TAG_OWNER * * * * * * * *
+ * | (skip if not needed) *
+ * v *
* SET_MIRROR_IMAGE * * * * * * *
* | *
* v *
void send_get_mirror_image();
Context *handle_get_mirror_image(int *result);
+ void send_get_features();
+ Context *handle_get_features(int *result);
+
void send_get_tag_owner();
Context *handle_get_tag_owner(int *result);
r = cls_client::mirror_image_get_finish(&iter, m_mirror_image);
}
- if (r == -ENOENT ||
- m_mirror_image->state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
+ if (r == -ENOENT) {
ldout(cct, 20) << "mirroring is disabled" << dendl;
- finish(0);
+ finish(r);
return;
} else if (r < 0) {
lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r)
return;
}
- get_tag_owner();
+ if (m_mirror_image->mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ get_journal_tag_owner();
+ } else if (m_mirror_image->mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ get_snapshot_promotion_state();
+ } else {
+ ldout(cct, 20) << "unknown mirror image mode: " << m_mirror_image->mode
+ << dendl;
+ finish(-EOPNOTSUPP);
+ }
}
template <typename I>
-void GetInfoRequest<I>::get_tag_owner() {
+void GetInfoRequest<I>::get_journal_tag_owner() {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << dendl;
auto ctx = create_context_callback<
- GetInfoRequest<I>, &GetInfoRequest<I>::handle_get_tag_owner>(this);
+ GetInfoRequest<I>, &GetInfoRequest<I>::handle_get_journal_tag_owner>(this);
Journal<I>::get_tag_owner(m_image_ctx.md_ctx, m_image_ctx.id,
&m_mirror_uuid, m_image_ctx.op_work_queue, ctx);
}
template <typename I>
-void GetInfoRequest<I>::handle_get_tag_owner(int r) {
+void GetInfoRequest<I>::handle_get_journal_tag_owner(int r) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << "r=" << r << dendl;
finish(0);
}
+template <typename I>
+void GetInfoRequest<I>::get_snapshot_promotion_state() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 20) << dendl;
+
+ *m_promotion_state = PROMOTION_STATE_PRIMARY;
+ {
+ std::shared_lock image_locker{m_image_ctx.image_lock};
+ for (auto it = m_image_ctx.snap_info.rbegin();
+ it != m_image_ctx.snap_info.rend(); it++) {
+ auto primary = boost::get<cls::rbd::MirrorPrimarySnapshotNamespace>(
+ &it->second.snap_namespace);
+ if (primary != nullptr) {
+ if (primary->demoted) {
+ *m_promotion_state = PROMOTION_STATE_ORPHAN;
+ }
+ break;
+ }
+ auto non_primary =
+ boost::get<cls::rbd::MirrorNonPrimarySnapshotNamespace>(
+ &it->second.snap_namespace);
+ if (non_primary != nullptr) {
+ if (non_primary->primary_mirror_uuid.empty()) {
+ *m_promotion_state = PROMOTION_STATE_ORPHAN;
+ } else {
+ *m_promotion_state = PROMOTION_STATE_NON_PRIMARY;
+ }
+ break;
+ }
+ }
+ }
+ finish(0);
+}
+
+
template <typename I>
void GetInfoRequest<I>::finish(int r) {
CephContext *cct = m_image_ctx.cct;
* GET_MIRROR_IMAGE
* |
* v
- * GET_TAG_OWNER
+ * GET_JOURNAL_TAG_OWNER (if journal)
* |
* v
* <finish>
void get_mirror_image();
void handle_get_mirror_image(int r);
- void get_tag_owner();
- void handle_get_tag_owner(int r);
+ void get_journal_tag_owner();
+ void handle_get_journal_tag_owner(int r);
+
+ void get_snapshot_promotion_state();
void finish(int r);
#include "librbd/Journal.h"
#include "librbd/Utils.h"
#include "librbd/mirror/GetInfoRequest.h"
+#include "librbd/mirror/snapshot/PromoteRequest.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
auto ctx = create_context_callback<
PromoteRequest<I>, &PromoteRequest<I>::handle_promote>(this);
- Journal<I>::promote(&m_image_ctx, ctx);
+ if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ Journal<I>::promote(&m_image_ctx, ctx);
+ } else if (m_mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ auto req = mirror::snapshot::PromoteRequest<I>::create(&m_image_ctx, ctx);
+ req->send();
+ } else {
+ lderr(cct) << "unknown image mirror mode: " << m_mirror_image.mode << dendl;
+ finish(-EOPNOTSUPP);
+ }
}
template <typename I>
template <typename ImageCtxT = librbd::ImageCtx>
class DemoteRequest {
public:
- static DemoteRequest *create(ImageCtxT *image_ctx, bool force,
- Context *on_finish) {
- return new DemoteRequest(image_ctx, force, on_finish);
+ static DemoteRequest *create(ImageCtxT *image_ctx, Context *on_finish) {
+ return new DemoteRequest(image_ctx, on_finish);
}
- DemoteRequest(ImageCtxT *image_ctx, bool force, Context *on_finish)
- : m_image_ctx(image_ctx), m_force(force), m_on_finish(on_finish) {
+ DemoteRequest(ImageCtxT *image_ctx, Context *on_finish)
+ : m_image_ctx(image_ctx), m_on_finish(on_finish) {
}
void send();
*/
ImageCtxT *m_image_ctx;
- bool m_force;
Context *m_on_finish;
void refresh_image();
template <typename ImageCtxT = librbd::ImageCtx>
class PromoteRequest {
public:
- static PromoteRequest *create(ImageCtxT *image_ctx, bool force,
- Context *on_finish) {
- return new PromoteRequest(image_ctx, force, on_finish);
+ static PromoteRequest *create(ImageCtxT *image_ctx, Context *on_finish) {
+ return new PromoteRequest(image_ctx, on_finish);
}
- PromoteRequest(ImageCtxT *image_ctx, bool force, Context *on_finish)
- : m_image_ctx(image_ctx), m_force(force), m_on_finish(on_finish) {
+ PromoteRequest(ImageCtxT *image_ctx, Context *on_finish)
+ : m_image_ctx(image_ctx), m_on_finish(on_finish) {
}
void send();
*/
ImageCtxT *m_image_ctx;
- bool m_force;
Context *m_on_finish;
void refresh_image();
#include "librbd/image/SetFlagsRequest.h"
#include "librbd/io/ImageRequestWQ.h"
#include "librbd/journal/CreateRequest.h"
+#include "librbd/mirror/DisableRequest.h"
#include "librbd/mirror/EnableRequest.h"
#include "librbd/object_map/CreateRequest.h"
CephContext *cct = image_ctx.cct;
if ((m_features & RBD_FEATURE_JOURNALING) == 0) {
- Context *ctx = create_context_callback<
- EnableFeaturesRequest<I>,
- &EnableFeaturesRequest<I>::handle_get_mirror_mode>(this);
- ctx->complete(-ENOENT);
+ send_get_mirror_image();
return;
}
CephContext *cct = image_ctx.cct;
ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl;
- cls::rbd::MirrorMode mirror_mode = cls::rbd::MIRROR_MODE_DISABLED;
if (*result == 0) {
auto it = m_out_bl.cbegin();
- *result = cls_client::mirror_mode_get_finish(&it, &mirror_mode);
+ *result = cls_client::mirror_mode_get_finish(&it, &m_mirror_mode);
} else if (*result == -ENOENT) {
*result = 0;
}
return handle_finish(*result);
}
- m_enable_mirroring = (mirror_mode == cls::rbd::MIRROR_MODE_POOL);
+ if (m_mirror_mode == cls::rbd::MIRROR_MODE_POOL) {
+ m_enable_mirroring = true;
+ }
+
+ send_get_mirror_image();
+ return nullptr;
+}
+
+template <typename I>
+void EnableFeaturesRequest<I>::send_get_mirror_image() {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+
+ if (m_mirror_mode != cls::rbd::MIRROR_MODE_IMAGE) {
+ Context *ctx = create_context_callback<
+ EnableFeaturesRequest<I>,
+ &EnableFeaturesRequest<I>::handle_get_mirror_image>(this);
+ ctx->complete(-ENOENT);
+ return;
+ }
+
+ ldout(cct, 20) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::mirror_image_get_start(&op, image_ctx.id);
+
+ using klass = EnableFeaturesRequest<I>;
+ librados::AioCompletion *comp =
+ create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
+ m_out_bl.clear();
+ int r = image_ctx.md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *EnableFeaturesRequest<I>::handle_get_mirror_image(int *result) {
+ I &image_ctx = this->m_image_ctx;
+ CephContext *cct = image_ctx.cct;
+ ldout(cct, 20) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ cls::rbd::MirrorImage mirror_image;
+ if (*result == 0) {
+ auto it = m_out_bl.cbegin();
+ *result = cls_client::mirror_image_get_finish(&it, &mirror_image);
+ } else if (*result == -ENOENT) {
+ *result = 0;
+ }
+
+ if (*result < 0) {
+ lderr(cct) << "failed to retrieve mirror image info: "
+ << cpp_strerror(*result) << dendl;
+ return handle_finish(*result);
+ }
+
+ if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
+ lderr(cct) << "cannot enable journaling: image snapshot mirroring enabled"
+ << dendl;
+ *result = -EINVAL;
+ return handle_finish(*result);
+ }
bool create_journal = false;
do {
void EnableFeaturesRequest<I>::send_enable_mirror_image() {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
- ldout(cct, 20) << this << " " << __func__ << dendl;
if (!m_enable_mirroring) {
send_notify_update();
return;
}
+ ldout(cct, 20) << this << " " << __func__ << dendl;
+
Context *ctx = create_context_callback<
EnableFeaturesRequest<I>,
&EnableFeaturesRequest<I>::handle_enable_mirror_image>(this);
#ifndef CEPH_LIBRBD_OPERATION_ENABLE_FEATURES_REQUEST_H
#define CEPH_LIBRBD_OPERATION_ENABLE_FEATURES_REQUEST_H
+#include "cls/rbd/cls_rbd_types.h"
+
#include "librbd/operation/Request.h"
class Context;
* STATE_BLOCK_WRITES
* |
* v
- * STATE_GET_MIRROR_MODE
- * |
+ * STATE_GET_MIRROR_MODE (skip if not
+ * | enabling journaling)
+ * v
+ * STATE_GET_MIRROR_IMAGE (skip if mirror mode
+ * | is not IMAGE)
* v
* STATE_CREATE_JOURNAL (skip if not
* | required)
* STATE_CREATE_OBJECT_MAP (skip if not
* | required)
* v
- * STATE_ENABLE_MIRROR_IMAGE
- * |
- * V
+ * STATE_ENABLE_MIRROR_IMAGE (skip if not
+ * | required)
+ * v
* STATE_NOTIFY_UPDATE
* |
* | (unblock writes)
*
*/
+ cls::rbd::MirrorMode m_mirror_mode = cls::rbd::MIRROR_MODE_DISABLED;
+
uint64_t m_features;
bool m_enable_mirroring = false;
void send_get_mirror_mode();
Context *handle_get_mirror_mode(int *result);
+ void send_get_mirror_image();
+ Context *handle_get_mirror_image(int *result);
+
void send_create_journal();
Context *handle_create_journal(int *result);
#include "librbd/MirroringWatcher.h"
#include "librbd/journal/PromoteRequest.h"
#include "librbd/mirror/DisableRequest.h"
+#include "librbd/mirror/GetInfoRequest.h"
namespace librbd {
} // anonymous namespace
-template <>
-struct Journal<librbd::MockTestImageCtx> {
- static Journal *s_instance;
- static void is_tag_owner(librbd::MockTestImageCtx *, bool *is_primary,
- Context *on_finish) {
- ceph_assert(s_instance != nullptr);
- s_instance->is_tag_owner(is_primary, on_finish);
- }
-
- Journal() {
- s_instance = this;
- }
-
- MOCK_METHOD2(is_tag_owner, void(bool*, Context*));
-};
-
-Journal<librbd::MockTestImageCtx> *Journal<librbd::MockTestImageCtx>::s_instance = nullptr;
-
template <>
struct MirroringWatcher<librbd::MockTestImageCtx> {
static MirroringWatcher *s_instance;
} // namespace journal
+namespace mirror {
+
+template <>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+ cls::rbd::MirrorImage *mirror_image;
+ PromotionState *promotion_state;
+ Context *on_finish = nullptr;
+ static GetInfoRequest *s_instance;
+ static GetInfoRequest *create(librbd::MockTestImageCtx &,
+ cls::rbd::MirrorImage *mirror_image,
+ PromotionState *promotion_state,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->mirror_image = mirror_image;
+ s_instance->promotion_state = promotion_state;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetInfoRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+GetInfoRequest<librbd::MockTestImageCtx> *GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
} // namespace librbd
// template definitions
#include "librbd/mirror/DisableRequest.cc"
template class librbd::mirror::DisableRequest<librbd::MockTestImageCtx>;
+ACTION_P(TestFeatures, image_ctx) {
+ return ((image_ctx->features & arg0) != 0);
+}
+
namespace librbd {
namespace mirror {
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
+using ::testing::Invoke;
using ::testing::Return;
-using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::WithArg;
typedef Journal<MockTestImageCtx> MockJournal;
typedef MirroringWatcher<MockTestImageCtx> MockMirroringWatcher;
typedef journal::PromoteRequest<MockTestImageCtx> MockPromoteRequest;
-
- void expect_get_mirror_image(MockTestImageCtx &mock_image_ctx,
- const cls::rbd::MirrorImage &mirror_image,
- int r) {
- using ceph::encode;
- bufferlist bl;
- encode(mirror_image, bl);
-
- EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
- exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_get"),
- _, _, _))
- .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)),
- Return(r)));
- }
-
- void expect_is_tag_owner(MockTestImageCtx &mock_image_ctx,
- MockJournal &mock_journal,
- bool is_primary, int r) {
- EXPECT_CALL(mock_journal, is_tag_owner(_, _))
- .WillOnce(DoAll(SetArgPointee<0>(is_primary),
- WithArg<1>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))));
+ typedef mirror::GetInfoRequest<MockTestImageCtx> MockGetInfoRequest;
+
+ void expect_get_mirror_info(MockTestImageCtx &mock_image_ctx,
+ MockGetInfoRequest &mock_get_info_request,
+ const cls::rbd::MirrorImage &mirror_image,
+ PromotionState promotion_state, int r) {
+
+ EXPECT_CALL(mock_get_info_request, send())
+ .WillOnce(
+ Invoke([this, &mock_image_ctx, &mock_get_info_request, mirror_image,
+ promotion_state, r]() {
+ if (r == 0) {
+ *mock_get_info_request.mirror_image = mirror_image;
+ *mock_get_info_request.promotion_state = promotion_state;
+ }
+ mock_image_ctx.op_work_queue->queue(
+ mock_get_info_request.on_finish, r);
+ }));
}
void expect_set_mirror_image(MockTestImageCtx &mock_image_ctx, int r) {
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
expect_op_work_queue(mock_image_ctx);
expect_snap_remove(mock_image_ctx, "snap 2", 0);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
MockPromoteRequest mock_promote_request;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
C_SaferCond ctx;
auto req = new MockDisableRequest(&mock_image_ctx, false, false, &ctx);
ASSERT_EQ(-EINVAL, ctx.wait());
}
-TEST_F(TestMockMirrorDisableRequest, MirrorImageGetError) {
+TEST_F(TestMockMirrorDisableRequest, GetMirrorInfoError) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(mock_image_ctx, {}, -EBADMSG);
-
- C_SaferCond ctx;
- auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
- req->send();
- ASSERT_EQ(-EBADMSG, ctx.wait());
-}
-
-TEST_F(TestMockMirrorDisableRequest, IsTagOwnerError) {
- REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
-
- expect_op_work_queue(mock_image_ctx);
-
- InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, -EBADMSG);
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, -EINVAL);
C_SaferCond ctx;
auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx);
req->send();
- ASSERT_EQ(-EBADMSG, ctx.wait());
+ ASSERT_EQ(-EINVAL, ctx.wait());
}
TEST_F(TestMockMirrorDisableRequest, MirrorImageSetError) {
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, -ENOENT);
C_SaferCond ctx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
MockPromoteRequest mock_promote_request;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_NON_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
expect_op_work_queue(mock_image_ctx);
expect_snap_remove(mock_image_ctx, "snap 2", -EPERM);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
expect_op_work_queue(mock_image_ctx);
expect_snap_remove(mock_image_ctx, "snap 2", 0);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockTestImageCtx mock_image_ctx(*ictx);
- MockJournal mock_journal;
MockMirroringWatcher mock_mirroring_watcher;
expect_op_work_queue(mock_image_ctx);
InSequence seq;
- expect_get_mirror_image(
- mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0);
- expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0);
+
+ MockGetInfoRequest mock_get_info_request;
+ expect_get_mirror_info(
+ mock_image_ctx, mock_get_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "global id",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, PROMOTION_STATE_PRIMARY, 0);
expect_set_mirror_image(mock_image_ctx, 0);
expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher,
cls::rbd::MIRROR_IMAGE_STATE_DISABLING,
} // namespace mirror
} // namespace librbd
-
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librbd/mock/MockJournalPolicy.h"
#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/api/Mirror.h"
#include "librbd/internal.h"
#include "librbd/image/SetFlagsRequest.h"
#include "librbd/io/AioCompletion.h"
typedef librbd::object_map::RemoveRequest<MockOperationImageCtx> MockRemoveObjectMapRequest;
typedef DisableFeaturesRequest<MockOperationImageCtx> MockDisableFeaturesRequest;
- class PoolMirrorModeEnabler {
+ class MirrorModeEnabler {
public:
- PoolMirrorModeEnabler(librados::IoCtx &ioctx) : m_ioctx(ioctx) {
+ MirrorModeEnabler(librados::IoCtx &ioctx, cls::rbd::MirrorMode mirror_mode)
+ : m_ioctx(ioctx), m_mirror_mode(mirror_mode) {
EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx, "test-uuid"));
- EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
- &m_ioctx, cls::rbd::MIRROR_MODE_POOL));
+ EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx, m_mirror_mode));
}
- ~PoolMirrorModeEnabler() {
+ ~MirrorModeEnabler() {
EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
- &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
+ &m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
}
private:
librados::IoCtx &m_ioctx;
+ cls::rbd::MirrorMode m_mirror_mode;
};
void expect_prepare_lock(MockOperationImageCtx &mock_image_ctx) {
ASSERT_EQ(-EINVAL, cond_ctx.wait());
}
-TEST_F(TestMockOperationDisableFeaturesRequest, Mirroring) {
+TEST_F(TestMockOperationDisableFeaturesRequest, PoolMirroring) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, cond_ctx.wait());
}
+TEST_F(TestMockOperationDisableFeaturesRequest, ImageMirroring) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_IMAGE);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(ictx, false));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockRemoveJournalRequest mock_remove_journal_request;
+ MockDisableMirrorRequest mock_disable_mirror_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_is_journal_replaying(*mock_image_ctx.journal);
+ expect_block_requests(mock_image_ctx);
+ expect_unblock_requests(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockDisableFeaturesRequest *req = new MockDisableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING, false);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
TEST_F(TestMockOperationDisableFeaturesRequest, MirroringError) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
#include "test/librbd/mock/MockImageCtx.h"
#include "cls/rbd/cls_rbd_client.h"
#include "librbd/Operations.h"
+#include "librbd/api/Mirror.h"
#include "librbd/internal.h"
#include "librbd/image/SetFlagsRequest.h"
#include "librbd/io/AioCompletion.h"
+#include "librbd/mirror/DisableRequest.h"
#include "librbd/mirror/EnableRequest.h"
#include "librbd/journal/CreateRequest.h"
#include "librbd/journal/Types.h"
typedef librbd::object_map::CreateRequest<MockOperationImageCtx> MockCreateObjectMapRequest;
typedef EnableFeaturesRequest<MockOperationImageCtx> MockEnableFeaturesRequest;
- class PoolMirrorModeEnabler {
+ class MirrorModeEnabler {
public:
- PoolMirrorModeEnabler(librados::IoCtx &ioctx) : m_ioctx(ioctx) {
+ MirrorModeEnabler(librados::IoCtx &ioctx, cls::rbd::MirrorMode mirror_mode)
+ : m_ioctx(ioctx), m_mirror_mode(mirror_mode) {
EXPECT_EQ(0, librbd::cls_client::mirror_uuid_set(&m_ioctx, "test-uuid"));
- EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
- &m_ioctx, cls::rbd::MIRROR_MODE_POOL));
+ EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(&m_ioctx, m_mirror_mode));
}
- ~PoolMirrorModeEnabler() {
+ ~MirrorModeEnabler() {
EXPECT_EQ(0, librbd::cls_client::mirror_mode_set(
&m_ioctx, cls::rbd::MIRROR_MODE_DISABLED));
}
private:
librados::IoCtx &m_ioctx;
+ cls::rbd::MirrorMode m_mirror_mode;
};
void ensure_features_disabled(librbd::ImageCtx *ictx,
ASSERT_EQ(-EINVAL, cond_ctx.wait());
}
-TEST_F(TestMockOperationEnableFeaturesRequest, Mirroring) {
+TEST_F(TestMockOperationEnableFeaturesRequest, PoolMirroring) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
- PoolMirrorModeEnabler enabler(m_ioctx);
-
MockOperationImageCtx mock_image_ctx(*ictx);
MockExclusiveLock mock_exclusive_lock;
MockJournal mock_journal;
ASSERT_EQ(0, cond_ctx.wait());
}
+TEST_F(TestMockOperationEnableFeaturesRequest, ImageMirroring) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+ ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
+
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_IMAGE);
+
+ ASSERT_EQ(0, librbd::api::Mirror<>::image_enable(ictx, false)); // snapshot mode
+
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+
+ MockOperationImageCtx mock_image_ctx(*ictx);
+ MockExclusiveLock mock_exclusive_lock;
+ MockJournal mock_journal;
+ MockObjectMap mock_object_map;
+ initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+ mock_object_map);
+
+ expect_verify_lock_ownership(mock_image_ctx);
+
+ MockCreateJournalRequest mock_create_journal_request;
+ MockEnableMirrorRequest mock_enable_mirror_request;
+
+ ::testing::InSequence seq;
+ expect_prepare_lock(mock_image_ctx);
+ expect_block_writes(mock_image_ctx);
+ expect_unblock_writes(mock_image_ctx);
+ expect_handle_prepare_lock_complete(mock_image_ctx);
+
+ C_SaferCond cond_ctx;
+ MockEnableFeaturesRequest *req = new MockEnableFeaturesRequest(
+ mock_image_ctx, &cond_ctx, 0, RBD_FEATURE_JOURNALING);
+ {
+ std::shared_lock owner_locker{mock_image_ctx.owner_lock};
+ req->send();
+ }
+ ASSERT_EQ(-EINVAL, cond_ctx.wait());
+}
+
TEST_F(TestMockOperationEnableFeaturesRequest, JournalingError) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
- PoolMirrorModeEnabler enabler(m_ioctx);
-
MockOperationImageCtx mock_image_ctx(*ictx);
MockExclusiveLock mock_exclusive_lock;
MockJournal mock_journal;
expect_verify_lock_ownership(mock_image_ctx);
MockCreateJournalRequest mock_create_journal_request;
- MockEnableMirrorRequest mock_enable_mirror_request;
::testing::InSequence seq;
expect_prepare_lock(mock_image_ctx);
TEST_F(TestMockOperationEnableFeaturesRequest, MirroringError) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+ MirrorModeEnabler mirror_mode_enabler(m_ioctx, cls::rbd::MIRROR_MODE_POOL);
+
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ensure_features_disabled(ictx, RBD_FEATURE_JOURNALING);
- PoolMirrorModeEnabler enabler(m_ioctx);
-
MockOperationImageCtx mock_image_ctx(*ictx);
MockExclusiveLock mock_exclusive_lock;
MockJournal mock_journal;
return 0;
}
- void check_mirror_image_enable(rbd_mirror_mode_t mirror_mode,
- uint64_t features,
- int expected_r,
- rbd_mirror_image_state_t mirror_state) {
+ void check_mirror_image_enable(
+ rbd_mirror_mode_t mirror_mode, uint64_t features, int expected_r,
+ rbd_mirror_image_state_t mirror_state) {
ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
ASSERT_EQ(mirror_state == RBD_MIRROR_IMAGE_ENABLED ? -ENOENT : -EINVAL,
image.mirror_image_get_instance_id(&instance_id));
- if (mirror_mode == RBD_MIRROR_MODE_IMAGE &&
- mirror_state == RBD_MIRROR_IMAGE_DISABLED) {
- // disabling image mirroring automatically disables journaling feature
- uint64_t new_features;
- ASSERT_EQ(0, image.features(&new_features));
- ASSERT_EQ(0, new_features & RBD_FEATURE_JOURNALING);
- }
-
ASSERT_EQ(0, 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));
ASSERT_EQ(mirror_images_new_count, mirror_images_count);
}
- void check_mirroring_on_update_features(uint64_t init_features,
- bool enable, bool enable_mirroring,
- uint64_t features, int expected_r,
- rbd_mirror_mode_t mirror_mode,
- rbd_mirror_image_state_t mirror_state) {
+ void check_mirroring_on_update_features(
+ uint64_t init_features, bool enable, bool enable_mirroring,
+ uint64_t features, int expected_r, rbd_mirror_mode_t mirror_mode,
+ rbd_mirror_image_state_t mirror_state) {
ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, mirror_mode));
RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_DISABLED);
}
+TEST_F(TestMirroring, EnableJournaling_In_MirrorModeImage_MirroringEnabled) {
+ uint64_t init_features = 0;
+ init_features |= RBD_FEATURE_OBJECT_MAP;
+ init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
+ uint64_t features = RBD_FEATURE_JOURNALING;
+ check_mirroring_on_update_features(init_features, true, true, features,
+ -EINVAL, RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED);
+}
+
TEST_F(TestMirroring, EnableJournaling_In_MirrorModePool) {
uint64_t init_features = 0;
init_features |= RBD_FEATURE_OBJECT_MAP;
init_features |= RBD_FEATURE_EXCLUSIVE_LOCK;
init_features |= RBD_FEATURE_JOURNALING;
uint64_t features = RBD_FEATURE_JOURNALING;
- check_mirroring_on_update_features(init_features, false, true, features, -EINVAL,
- RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED);
+ check_mirroring_on_update_features(init_features, false, true, features,
+ -EINVAL, RBD_MIRROR_MODE_IMAGE, RBD_MIRROR_IMAGE_ENABLED);
}
TEST_F(TestMirroring, MirrorModeSet_DisabledMode_To_PoolMode) {
#include "test/rbd_mirror/test_mock_fixture.h"
#include "librbd/journal/TypeTraits.h"
+#include "librbd/mirror/GetInfoRequest.h"
#include "tools/rbd_mirror/InstanceWatcher.h"
#include "tools/rbd_mirror/Threads.h"
#include "tools/rbd_mirror/image_replayer/BootstrapRequest.h"
#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
#include "tools/rbd_mirror/image_replayer/CreateImageRequest.h"
-#include "tools/rbd_mirror/image_replayer/IsPrimaryRequest.h"
#include "tools/rbd_mirror/image_replayer/OpenImageRequest.h"
#include "tools/rbd_mirror/image_replayer/OpenLocalImageRequest.h"
#include "test/journal/mock/MockJournaler.h"
} // namespace journal
+namespace mirror {
+
+template<>
+struct GetInfoRequest<librbd::MockTestImageCtx> {
+ static GetInfoRequest* s_instance;
+ cls::rbd::MirrorImage *mirror_image;
+ PromotionState *promotion_state;
+ Context *on_finish = nullptr;
+
+ static GetInfoRequest* create(librbd::MockTestImageCtx &image_ctx,
+ cls::rbd::MirrorImage *mirror_image,
+ PromotionState *promotion_state,
+ Context *on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->mirror_image = mirror_image;
+ s_instance->promotion_state = promotion_state;
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ GetInfoRequest() {
+ ceph_assert(s_instance == nullptr);
+ s_instance = this;
+ }
+ ~GetInfoRequest() {
+ s_instance = nullptr;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
+GetInfoRequest<librbd::MockTestImageCtx>* GetInfoRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+
namespace util {
static std::string s_image_id;
MOCK_METHOD0(send, void());
};
-template<>
-struct IsPrimaryRequest<librbd::MockTestImageCtx> {
- static IsPrimaryRequest* s_instance;
- bool *primary = nullptr;
- Context *on_finish = nullptr;
-
- static IsPrimaryRequest* create(librbd::MockTestImageCtx *image_ctx,
- bool *primary, Context *on_finish) {
- ceph_assert(s_instance != nullptr);
- s_instance->primary = primary;
- s_instance->on_finish = on_finish;
- return s_instance;
- }
-
- IsPrimaryRequest() {
- ceph_assert(s_instance == nullptr);
- s_instance = this;
- }
- ~IsPrimaryRequest() {
- s_instance = nullptr;
- }
-
- MOCK_METHOD0(send, void());
-};
-
template<>
struct OpenImageRequest<librbd::MockTestImageCtx> {
static OpenImageRequest* s_instance;
CloseImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
CreateImageRequest<librbd::MockTestImageCtx>*
CreateImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
-IsPrimaryRequest<librbd::MockTestImageCtx>*
- IsPrimaryRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
OpenImageRequest<librbd::MockTestImageCtx>*
OpenImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
OpenLocalImageRequest<librbd::MockTestImageCtx>*
typedef CreateImageRequest<librbd::MockTestImageCtx> MockCreateImageRequest;
typedef ImageSync<librbd::MockTestImageCtx> MockImageSync;
typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
- typedef IsPrimaryRequest<librbd::MockTestImageCtx> MockIsPrimaryRequest;
typedef OpenImageRequest<librbd::MockTestImageCtx> MockOpenImageRequest;
typedef OpenLocalImageRequest<librbd::MockTestImageCtx> MockOpenLocalImageRequest;
+ typedef librbd::mirror::GetInfoRequest<librbd::MockTestImageCtx> MockGetMirrorInfoRequest;
typedef std::list<cls::journal::Tag> Tags;
void SetUp() override {
}));
}
- void expect_is_primary(MockIsPrimaryRequest &mock_is_primary_request,
- bool primary, int r) {
- EXPECT_CALL(mock_is_primary_request, send())
- .WillOnce(Invoke([this, &mock_is_primary_request, primary, r]() {
- *mock_is_primary_request.primary = primary;
- m_threads->work_queue->queue(mock_is_primary_request.on_finish, r);
+ void expect_get_remote_mirror_info(
+ MockGetMirrorInfoRequest &mock_get_mirror_info_request,
+ const cls::rbd::MirrorImage &mirror_image,
+ librbd::mirror::PromotionState promotion_state, int r) {
+ EXPECT_CALL(mock_get_mirror_info_request, send())
+ .WillOnce(Invoke([this, &mock_get_mirror_info_request, mirror_image,
+ promotion_state, r]() {
+ *mock_get_mirror_info_request.mirror_image = mirror_image;
+ *mock_get_mirror_info_request.promotion_state = promotion_state;
+ m_threads->work_queue->queue(
+ mock_get_mirror_info_request.on_finish, r);
}));
}
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, false, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY, 0);
// switch the state to replaying
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, false, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY, 0);
// open the local image
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, false, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY, 0);
// open the local image
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, true, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// open the local image
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, true, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// open the local image
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, true, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// open the local image
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, true, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// open the local image
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, true, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// update client state back to syncing
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, true, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// open the missing local image
MockOpenLocalImageRequest mock_open_local_image_request;
expect_journaler_register_client(mock_journaler, client_data, 0);
// test if remote image is primary
- expect_is_primary(mock_is_primary_request, true, 0);
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// update client state back to syncing
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
// test if remote image is primary
- MockIsPrimaryRequest mock_is_primary_request;
- expect_is_primary(mock_is_primary_request, true, 0);
+ MockGetMirrorInfoRequest mock_get_mirror_info_request;
+ expect_get_remote_mirror_info(mock_get_mirror_info_request,
+ {cls::rbd::MIRROR_IMAGE_MODE_JOURNAL, "uuid",
+ cls::rbd::MIRROR_IMAGE_STATE_ENABLED},
+ librbd::mirror::PROMOTION_STATE_PRIMARY, 0);
// update client state back to syncing
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
image_replayer/CreateImageRequest.cc
image_replayer/EventPreprocessor.cc
image_replayer/GetMirrorImageIdRequest.cc
- image_replayer/IsPrimaryRequest.cc
image_replayer/OpenImageRequest.cc
image_replayer/OpenLocalImageRequest.cc
image_replayer/PrepareLocalImageRequest.cc
#include "BootstrapRequest.h"
#include "CloseImageRequest.h"
#include "CreateImageRequest.h"
-#include "IsPrimaryRequest.h"
#include "OpenImageRequest.h"
#include "OpenLocalImageRequest.h"
#include "common/debug.h"
#include "librbd/Journal.h"
#include "librbd/Utils.h"
#include "librbd/journal/Types.h"
+#include "librbd/mirror/GetInfoRequest.h"
#include "tools/rbd_mirror/ProgressContext.h"
#include "tools/rbd_mirror/ImageSync.h"
#include "tools/rbd_mirror/Threads.h"
return;
}
- is_primary();
+ get_remote_mirror_info();
}
template <typename I>
-void BootstrapRequest<I>::is_primary() {
+void BootstrapRequest<I>::get_remote_mirror_info() {
dout(15) << dendl;
- update_progress("OPEN_REMOTE_IMAGE");
+ update_progress("GET_REMOTE_MIRROR_INFO");
Context *ctx = create_context_callback<
- BootstrapRequest<I>, &BootstrapRequest<I>::handle_is_primary>(
+ BootstrapRequest<I>, &BootstrapRequest<I>::handle_get_remote_mirror_info>(
this);
- IsPrimaryRequest<I> *request = IsPrimaryRequest<I>::create(m_remote_image_ctx,
- &m_primary, ctx);
+ auto request = librbd::mirror::GetInfoRequest<I>::create(
+ *m_remote_image_ctx, &m_mirror_image, &m_promotion_state, ctx);
request->send();
}
template <typename I>
-void BootstrapRequest<I>::handle_is_primary(int r) {
+void BootstrapRequest<I>::handle_get_remote_mirror_info(int r) {
dout(15) << "r=" << r << dendl;
if (r == -ENOENT) {
return;
}
- if (!m_primary) {
+ if (m_mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
+ dout(5) << "remote image mirroring is being disabled" << dendl;
+ m_ret_val = -EREMOTEIO;
+ close_remote_image();
+ return;
+ }
+
+ if (m_mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ dout(5) << ": remote image is in unsupported mode: " << m_mirror_image.mode
+ << dendl;
+ m_ret_val = -EOPNOTSUPP;
+ close_remote_image();
+ return;
+ }
+
+ if (m_promotion_state != librbd::mirror::PROMOTION_STATE_PRIMARY) {
if (m_local_image_id.empty()) {
// no local image and remote isn't primary -- don't sync it
dout(5) << "remote image is not primary -- not syncing"
local_image_ctx->image_lock.unlock_shared();
}
- if (m_local_tag_data.mirror_uuid != m_remote_mirror_uuid && !m_primary) {
+ if (m_local_tag_data.mirror_uuid != m_remote_mirror_uuid &&
+ m_promotion_state != librbd::mirror::PROMOTION_STATE_PRIMARY) {
// if the local mirror is not linked to the (now) non-primary image,
// stop the replay. Otherwise, we ignore that the remote is non-primary
// so that we can replay the demotion
*m_client_meta = librbd::journal::MirrorPeerClientMeta();
m_client_meta->state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
- is_primary();
+ get_remote_mirror_info();
}
template <typename I>
#include "include/rados/librados.hpp"
#include "common/ceph_mutex.h"
#include "cls/journal/cls_journal_types.h"
+#include "cls/rbd/cls_rbd_types.h"
#include "librbd/journal/Types.h"
#include "librbd/journal/TypeTraits.h"
+#include "librbd/mirror/Types.h"
#include "tools/rbd_mirror/BaseRequest.h"
#include "tools/rbd_mirror/Types.h"
#include <list>
* | *
* |/--------------------------------------------------*---\
* v * |
- * IS_PRIMARY * * * * * * * * * * * * * * * * * * * * * * |
+ * GET_REMOTE_MIRROR_INFO * * * * * * * * * * * * * * * * |
* | * * |
* | (remote image primary, no local image id) * * |
* \----> UPDATE_CLIENT_IMAGE * * * * * * * * * * * * |
cls::journal::Client m_client;
uint64_t m_remote_tag_class = 0;
ImageCtxT *m_remote_image_ctx = nullptr;
- bool m_primary = false;
+ cls::rbd::MirrorImage m_mirror_image;
+ librbd::mirror::PromotionState m_promotion_state =
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY;
int m_ret_val = 0;
ImageSync<ImageCtxT> *m_image_sync = nullptr;
void open_remote_image();
void handle_open_remote_image(int r);
- void is_primary();
- void handle_is_primary(int r);
+ void get_remote_mirror_info();
+ void handle_get_remote_mirror_info(int r);
void update_client_state();
void handle_update_client_state(int r);
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#include "IsPrimaryRequest.h"
-#include "common/debug.h"
-#include "common/errno.h"
-#include "common/WorkQueue.h"
-#include "cls/rbd/cls_rbd_client.h"
-#include "librbd/ImageCtx.h"
-#include "librbd/Journal.h"
-#include "librbd/Utils.h"
-#include <type_traits>
-
-#define dout_context g_ceph_context
-#define dout_subsys ceph_subsys_rbd_mirror
-#undef dout_prefix
-#define dout_prefix *_dout << "rbd::mirror::image_replayer::IsPrimaryRequest: " \
- << this << " " << __func__ << " "
-
-namespace rbd {
-namespace mirror {
-namespace image_replayer {
-
-using librbd::util::create_context_callback;
-using librbd::util::create_rados_callback;
-
-template <typename I>
-IsPrimaryRequest<I>::IsPrimaryRequest(I *image_ctx, bool *primary,
- Context *on_finish)
- : m_image_ctx(image_ctx), m_primary(primary), m_on_finish(on_finish) {
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::send() {
- send_get_mirror_state();
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::send_get_mirror_state() {
- dout(20) << dendl;
-
- librados::ObjectReadOperation op;
- librbd::cls_client::mirror_image_get_start(&op, m_image_ctx->id);
-
- librados::AioCompletion *aio_comp = create_rados_callback<
- IsPrimaryRequest<I>, &IsPrimaryRequest<I>::handle_get_mirror_state>(this);
- int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, aio_comp, &op,
- &m_out_bl);
- ceph_assert(r == 0);
- aio_comp->release();
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::handle_get_mirror_state(int r) {
- dout(20) << ": r=" << r << dendl;
-
- cls::rbd::MirrorImage mirror_image;
- if (r == 0) {
- auto iter = m_out_bl.cbegin();
- r = librbd::cls_client::mirror_image_get_finish(&iter, &mirror_image);
- if (r == 0) {
- if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
- if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
- send_is_tag_owner();
- } else if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
- // TODO: get primary state from mirroring snapshots
- ceph_abort();
- finish(0);
- }
- return;
- } else if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
- dout(5) << ": image mirroring is being disabled" << dendl;
- r = -ENOENT;
- } else {
- derr << ": image mirroring is disabled" << dendl;
- r = -EINVAL;
- }
- } else {
- derr << ": failed to decode image mirror state: " << cpp_strerror(r)
- << dendl;
- }
- } else if (r == -ENOENT) {
- dout(5) << ": image is not mirrored" << dendl;
- } else {
- derr << ": failed to retrieve image mirror state: " << cpp_strerror(r)
- << dendl;
- }
-
- finish(r);
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::send_is_tag_owner() {
- // deduce the class type for the journal to support unit tests
- using Journal = typename std::decay<
- typename std::remove_pointer<decltype(std::declval<I>().journal)>
- ::type>::type;
-
- dout(20) << dendl;
-
- Context *ctx = create_context_callback<
- IsPrimaryRequest<I>, &IsPrimaryRequest<I>::handle_is_tag_owner>(this);
-
- Journal::is_tag_owner(m_image_ctx, m_primary, ctx);
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::handle_is_tag_owner(int r) {
- dout(20) << ": r=" << r << dendl;
-
- if (r < 0) {
- derr << ": failed to query remote image tag owner: " << cpp_strerror(r)
- << dendl;
- }
-
- finish(r);
-}
-
-template <typename I>
-void IsPrimaryRequest<I>::finish(int r) {
- dout(20) << ": r=" << r << dendl;
-
- m_on_finish->complete(r);
- delete this;
-}
-
-} // namespace image_replayer
-} // namespace mirror
-} // namespace rbd
-
-template class rbd::mirror::image_replayer::IsPrimaryRequest<librbd::ImageCtx>;
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#ifndef RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H
-#define RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H
-
-#include "include/buffer.h"
-
-class Context;
-class ContextWQ;
-namespace librbd { class ImageCtx; }
-
-namespace rbd {
-namespace mirror {
-namespace image_replayer {
-
-template <typename ImageCtxT = librbd::ImageCtx>
-class IsPrimaryRequest {
-public:
- static IsPrimaryRequest* create(ImageCtxT *image_ctx, bool *primary,
- Context *on_finish) {
- return new IsPrimaryRequest(image_ctx, primary, on_finish);
- }
-
- IsPrimaryRequest(ImageCtxT *image_ctx, bool *primary, Context *on_finish);
-
- void send();
-
-private:
- /**
- * @verbatim
- *
- * <start>
- * |
- * v
- * GET_MIRROR_STATE * * * * *
- * | *
- * v *
- * IS_TAG_OWNER * * * * * * * (error)
- * | *
- * v *
- * <finish> < * * * * * * * *
- *
- * @endverbatim
- */
- ImageCtxT *m_image_ctx;
- bool *m_primary;
- Context *m_on_finish;
-
- bufferlist m_out_bl;
-
- void send_get_mirror_state();
- void handle_get_mirror_state(int r);
-
- void send_is_tag_owner();
- void handle_is_tag_owner(int r);
-
- void finish(int r);
-};
-
-} // namespace image_replayer
-} // namespace mirror
-} // namespace rbd
-
-extern template class rbd::mirror::image_replayer::IsPrimaryRequest<librbd::ImageCtx>;
-
-#endif // RBD_MIRROR_IMAGE_REPLAYER_IS_PRIMARY_REQUEST_H
#include "include/compat.h"
#include "CloseImageRequest.h"
-#include "IsPrimaryRequest.h"
#include "OpenLocalImageRequest.h"
#include "common/debug.h"
#include "common/errno.h"
#include "librbd/Utils.h"
#include "librbd/exclusive_lock/Policy.h"
#include "librbd/journal/Policy.h"
+#include "librbd/mirror/GetInfoRequest.h"
#include <type_traits>
#define dout_context g_ceph_context
return;
}
- send_is_primary();
+ send_get_mirror_info();
}
template <typename I>
-void OpenLocalImageRequest<I>::send_is_primary() {
+void OpenLocalImageRequest<I>::send_get_mirror_info() {
dout(20) << dendl;
Context *ctx = create_context_callback<
- OpenLocalImageRequest<I>, &OpenLocalImageRequest<I>::handle_is_primary>(
+ OpenLocalImageRequest<I>,
+ &OpenLocalImageRequest<I>::handle_get_mirror_info>(
this);
- IsPrimaryRequest<I> *request = IsPrimaryRequest<I>::create(*m_local_image_ctx,
- &m_primary, ctx);
+ auto request = librbd::mirror::GetInfoRequest<I>::create(
+ **m_local_image_ctx, &m_mirror_image, &m_promotion_state, ctx);
request->send();
}
template <typename I>
-void OpenLocalImageRequest<I>::handle_is_primary(int r) {
+void OpenLocalImageRequest<I>::handle_get_mirror_info(int r) {
dout(20) << ": r=" << r << dendl;
if (r == -ENOENT) {
return;
}
+ if (m_mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
+ dout(5) << ": local image mirroring is being disabled" << dendl;
+ send_close_image(-ENOENT);
+ return;
+ }
+
+ if (m_mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
+ dout(5) << ": local image is in unsupported mode: " << m_mirror_image.mode
+ << dendl;
+ send_close_image(-EOPNOTSUPP);
+ return;
+ }
+
// if the local image owns the tag -- don't steal the lock since
// we aren't going to mirror peer data into this image anyway
- if (m_primary) {
+ if (m_promotion_state == librbd::mirror::PROMOTION_STATE_PRIMARY) {
dout(10) << ": local image is primary -- skipping image replay" << dendl;
send_close_image(-EREMOTEIO);
return;
#define RBD_MIRROR_IMAGE_REPLAYER_OPEN_LOCAL_IMAGE_REQUEST_H
#include "include/int_types.h"
+#include "cls/rbd/cls_rbd_types.h"
#include "librbd/ImageCtx.h"
+#include "librbd/mirror/Types.h"
#include <string>
class Context;
* OPEN_IMAGE * * * * * * * *
* | *
* v *
- * IS_PRIMARY * * * * * * * *
+ * GET_MIRROR_INFO * * * * *
* | *
* v (skip if primary) v
* LOCK_IMAGE * * * > CLOSE_IMAGE
ContextWQ *m_work_queue;
Context *m_on_finish;
- bool m_primary = false;
+ cls::rbd::MirrorImage m_mirror_image;
+ librbd::mirror::PromotionState m_promotion_state =
+ librbd::mirror::PROMOTION_STATE_NON_PRIMARY;
int m_ret_val = 0;
void send_open_image();
void handle_open_image(int r);
- void send_is_primary();
- void handle_is_primary(int r);
+ void send_get_mirror_info();
+ void handle_get_mirror_info(int r);
void send_lock_image();
void handle_lock_image(int r);