mirror of
https://github.com/ceph/ceph
synced 2025-02-24 03:27:10 +00:00
Merge pull request #33490 from dillaman/wip-43934
rbd-mirror: add support for snapshot-based mirroring resyncs Reviewed-by: Mykola Golub <mgolub@suse.com>
This commit is contained in:
commit
f1c7155713
@ -1522,23 +1522,42 @@ int metadata_list_finish(bufferlist::const_iterator *it,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void metadata_get_start(librados::ObjectReadOperation* op,
|
||||
const std::string &key) {
|
||||
bufferlist bl;
|
||||
encode(key, bl);
|
||||
|
||||
op->exec("rbd", "metadata_get", bl);
|
||||
}
|
||||
|
||||
int metadata_get_finish(bufferlist::const_iterator *it,
|
||||
std::string* value) {
|
||||
try {
|
||||
decode(*value, *it);
|
||||
} catch (const buffer::error &err) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int metadata_get(librados::IoCtx *ioctx, const std::string &oid,
|
||||
const std::string &key, string *s)
|
||||
{
|
||||
ceph_assert(s);
|
||||
bufferlist in, out;
|
||||
encode(key, in);
|
||||
int r = ioctx->exec(oid, "rbd", "metadata_get", in, out);
|
||||
if (r < 0)
|
||||
return r;
|
||||
librados::ObjectReadOperation op;
|
||||
metadata_get_start(&op, key);
|
||||
|
||||
auto iter = out.cbegin();
|
||||
try {
|
||||
decode(*s, iter);
|
||||
} catch (const buffer::error &err) {
|
||||
return -EBADMSG;
|
||||
bufferlist out_bl;
|
||||
int r = ioctx->operate(oid, &op, &out_bl);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto it = out_bl.cbegin();
|
||||
r = metadata_get_finish(&it, s);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -249,6 +249,10 @@ void metadata_remove(librados::ObjectWriteOperation *op,
|
||||
const std::string &key);
|
||||
int metadata_remove(librados::IoCtx *ioctx, const std::string &oid,
|
||||
const std::string &key);
|
||||
void metadata_get_start(librados::ObjectReadOperation* op,
|
||||
const std::string &key);
|
||||
int metadata_get_finish(bufferlist::const_iterator *it,
|
||||
std::string* value);
|
||||
int metadata_get(librados::IoCtx *ioctx, const std::string &oid,
|
||||
const std::string &key, string *v);
|
||||
|
||||
|
@ -112,6 +112,7 @@ set(librbd_internal_srcs
|
||||
mirror/snapshot/CreatePrimaryRequest.cc
|
||||
mirror/snapshot/DemoteRequest.cc
|
||||
mirror/snapshot/GetImageStateRequest.cc
|
||||
mirror/snapshot/ImageMeta.cc
|
||||
mirror/snapshot/PromoteRequest.cc
|
||||
mirror/snapshot/RemoveImageStateRequest.cc
|
||||
mirror/snapshot/SetImageStateRequest.cc
|
||||
|
@ -172,8 +172,9 @@ int Config<I>::list(I *image_ctx, std::vector<config_option_t> *options) {
|
||||
std::map<std::string, bufferlist> pairs;
|
||||
C_SaferCond ctx;
|
||||
auto req = image::GetMetadataRequest<I>::create(
|
||||
image_ctx->md_ctx, image_ctx->header_oid, ImageCtx::METADATA_CONF_PREFIX,
|
||||
ImageCtx::METADATA_CONF_PREFIX, 0U, &pairs, &ctx);
|
||||
image_ctx->md_ctx, image_ctx->header_oid, true,
|
||||
ImageCtx::METADATA_CONF_PREFIX, ImageCtx::METADATA_CONF_PREFIX, 0U, &pairs,
|
||||
&ctx);
|
||||
req->send();
|
||||
|
||||
r = ctx.wait();
|
||||
|
@ -26,7 +26,9 @@
|
||||
#include "librbd/mirror/Types.h"
|
||||
#include "librbd/MirroringWatcher.h"
|
||||
#include "librbd/mirror/snapshot/CreatePrimaryRequest.h"
|
||||
#include "librbd/mirror/snapshot/ImageMeta.h"
|
||||
#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
|
||||
#include "librbd/mirror/snapshot/Utils.h"
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/scope_exit.hpp>
|
||||
@ -473,89 +475,107 @@ int Mirror<I>::image_disable(I *ictx, bool force) {
|
||||
if (r < 0) {
|
||||
lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
|
||||
return r;
|
||||
} else {
|
||||
bool rollback = false;
|
||||
BOOST_SCOPE_EXIT_ALL(ictx, &mirror_image_internal, &rollback) {
|
||||
if (rollback) {
|
||||
CephContext *cct = ictx->cct;
|
||||
mirror_image_internal.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
|
||||
int r = cls_client::mirror_image_set(&ictx->md_ctx, ictx->id,
|
||||
mirror_image_internal);
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to re-enable image mirroring: "
|
||||
<< cpp_strerror(r) << dendl;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_lock l{ictx->image_lock};
|
||||
map<librados::snap_t, SnapInfo> snap_info = ictx->snap_info;
|
||||
for (auto &info : snap_info) {
|
||||
cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
|
||||
ictx->md_ctx.get_namespace(),
|
||||
ictx->id, info.first};
|
||||
std::vector<librbd::linked_image_spec_t> child_images;
|
||||
r = Image<I>::list_children(ictx, parent_spec, &child_images);
|
||||
bool rollback = false;
|
||||
BOOST_SCOPE_EXIT_ALL(ictx, &mirror_image_internal, &rollback) {
|
||||
if (rollback) {
|
||||
CephContext *cct = ictx->cct;
|
||||
mirror_image_internal.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
|
||||
int r = cls_client::mirror_image_set(&ictx->md_ctx, ictx->id,
|
||||
mirror_image_internal);
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to re-enable image mirroring: "
|
||||
<< cpp_strerror(r) << dendl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_lock image_locker{ictx->image_lock};
|
||||
map<librados::snap_t, SnapInfo> snap_info = ictx->snap_info;
|
||||
for (auto &info : snap_info) {
|
||||
cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
|
||||
ictx->md_ctx.get_namespace(),
|
||||
ictx->id, info.first};
|
||||
std::vector<librbd::linked_image_spec_t> child_images;
|
||||
r = Image<I>::list_children(ictx, parent_spec, &child_images);
|
||||
if (r < 0) {
|
||||
rollback = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (child_images.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
librados::IoCtx child_io_ctx;
|
||||
int64_t child_pool_id = -1;
|
||||
for (auto &child_image : child_images){
|
||||
std::string pool = child_image.pool_name;
|
||||
if (child_pool_id == -1 ||
|
||||
child_pool_id != child_image.pool_id ||
|
||||
child_io_ctx.get_namespace() != child_image.pool_namespace) {
|
||||
r = util::create_ioctx(ictx->md_ctx, "child image",
|
||||
child_image.pool_id,
|
||||
child_image.pool_namespace,
|
||||
&child_io_ctx);
|
||||
if (r < 0) {
|
||||
rollback = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (child_images.empty()) {
|
||||
continue;
|
||||
}
|
||||
child_pool_id = child_image.pool_id;
|
||||
}
|
||||
|
||||
librados::IoCtx child_io_ctx;
|
||||
int64_t child_pool_id = -1;
|
||||
for (auto &child_image : child_images){
|
||||
std::string pool = child_image.pool_name;
|
||||
if (child_pool_id == -1 ||
|
||||
child_pool_id != child_image.pool_id ||
|
||||
child_io_ctx.get_namespace() != child_image.pool_namespace) {
|
||||
r = util::create_ioctx(ictx->md_ctx, "child image",
|
||||
child_image.pool_id,
|
||||
child_image.pool_namespace,
|
||||
&child_io_ctx);
|
||||
if (r < 0) {
|
||||
rollback = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
child_pool_id = child_image.pool_id;
|
||||
}
|
||||
|
||||
cls::rbd::MirrorImage mirror_image_internal;
|
||||
r = cls_client::mirror_image_get(&child_io_ctx, child_image.image_id,
|
||||
&mirror_image_internal);
|
||||
if (r != -ENOENT) {
|
||||
rollback = true;
|
||||
lderr(cct) << "mirroring is enabled on one or more children "
|
||||
<< dendl;
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
cls::rbd::MirrorImage child_mirror_image_internal;
|
||||
r = cls_client::mirror_image_get(&child_io_ctx, child_image.image_id,
|
||||
&child_mirror_image_internal);
|
||||
if (r != -ENOENT) {
|
||||
rollback = true;
|
||||
lderr(cct) << "mirroring is enabled on one or more children "
|
||||
<< dendl;
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
image_locker.unlock();
|
||||
|
||||
C_SaferCond ctx;
|
||||
auto req = mirror::DisableRequest<ImageCtx>::create(ictx, force, true,
|
||||
&ctx);
|
||||
req->send();
|
||||
|
||||
r = ctx.wait();
|
||||
if (mirror_image_internal.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
|
||||
// remove any snapshot-based mirroring image-meta from image
|
||||
std::string mirror_uuid;
|
||||
r = uuid_get(ictx->md_ctx, &mirror_uuid);
|
||||
if (r < 0) {
|
||||
lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
|
||||
rollback = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (mirror_image_internal.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
|
||||
r = ictx->operations->update_features(RBD_FEATURE_JOURNALING, false);
|
||||
if (r < 0) {
|
||||
lderr(cct) << "cannot disable journaling: " << cpp_strerror(r) << dendl;
|
||||
// not fatal
|
||||
}
|
||||
r = ictx->operations->metadata_remove(
|
||||
mirror::snapshot::util::get_image_meta_key(mirror_uuid));
|
||||
if (r < 0 && r != -ENOENT) {
|
||||
lderr(cct) << "cannot remove snapshot image-meta key: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
rollback = true;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
C_SaferCond ctx;
|
||||
auto req = mirror::DisableRequest<ImageCtx>::create(ictx, force, true,
|
||||
&ctx);
|
||||
req->send();
|
||||
|
||||
r = ctx.wait();
|
||||
if (r < 0) {
|
||||
lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
|
||||
rollback = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (mirror_image_internal.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
|
||||
r = ictx->operations->update_features(RBD_FEATURE_JOURNALING, false);
|
||||
if (r < 0) {
|
||||
lderr(cct) << "cannot disable journaling: " << cpp_strerror(r) << dendl;
|
||||
// not fatal
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,24 +667,63 @@ int Mirror<I>::image_resync(I *ictx) {
|
||||
return r;
|
||||
}
|
||||
|
||||
C_SaferCond tag_owner_ctx;
|
||||
bool is_tag_owner;
|
||||
Journal<I>::is_tag_owner(ictx, &is_tag_owner, &tag_owner_ctx);
|
||||
r = tag_owner_ctx.wait();
|
||||
cls::rbd::MirrorImage mirror_image;
|
||||
mirror::PromotionState promotion_state;
|
||||
std::string primary_mirror_uuid;
|
||||
C_SaferCond get_info_ctx;
|
||||
auto req = mirror::GetInfoRequest<I>::create(*ictx, &mirror_image,
|
||||
&promotion_state,
|
||||
&primary_mirror_uuid,
|
||||
&get_info_ctx);
|
||||
req->send();
|
||||
|
||||
r = get_info_ctx.wait();
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to determine tag ownership: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
return r;
|
||||
} else if (is_tag_owner) {
|
||||
}
|
||||
|
||||
if (promotion_state == mirror::PROMOTION_STATE_PRIMARY) {
|
||||
lderr(cct) << "image is primary, cannot resync to itself" << dendl;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// flag the journal indicating that we want to rebuild the local image
|
||||
r = Journal<I>::request_resync(ictx);
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to request resync: " << cpp_strerror(r) << dendl;
|
||||
return r;
|
||||
if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
|
||||
// flag the journal indicating that we want to rebuild the local image
|
||||
r = Journal<I>::request_resync(ictx);
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to request resync: " << cpp_strerror(r) << dendl;
|
||||
return r;
|
||||
}
|
||||
} else if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
|
||||
std::string mirror_uuid;
|
||||
r = uuid_get(ictx->md_ctx, &mirror_uuid);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
mirror::snapshot::ImageMeta image_meta(ictx, mirror_uuid);
|
||||
|
||||
C_SaferCond load_meta_ctx;
|
||||
image_meta.load(&load_meta_ctx);
|
||||
r = load_meta_ctx.wait();
|
||||
if (r < 0 && r != -ENOENT) {
|
||||
lderr(cct) << "failed to load mirror image-meta: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
return r;
|
||||
}
|
||||
|
||||
image_meta.resync_requested = true;
|
||||
|
||||
C_SaferCond save_meta_ctx;
|
||||
image_meta.save(&save_meta_ctx);
|
||||
r = save_meta_ctx.wait();
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to request resync: " << cpp_strerror(r) << dendl;
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
lderr(cct) << "unknown mirror mode" << dendl;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -99,7 +99,7 @@ int PoolMetadata<I>::list(librados::IoCtx& io_ctx, const std::string &start,
|
||||
pairs->clear();
|
||||
C_SaferCond ctx;
|
||||
auto req = image::GetMetadataRequest<I>::create(
|
||||
io_ctx, RBD_INFO, "", start, max, pairs, &ctx);
|
||||
io_ctx, RBD_INFO, false, "", start, max, pairs, &ctx);
|
||||
req->send();
|
||||
|
||||
int r = ctx.wait();
|
||||
|
@ -46,7 +46,7 @@ void MetadataCopyRequest<I>::list_src_metadata() {
|
||||
MetadataCopyRequest<I>,
|
||||
&MetadataCopyRequest<I>::handle_list_src_metadata>(this);
|
||||
auto req = image::GetMetadataRequest<I>::create(
|
||||
m_src_image_ctx->md_ctx, m_src_image_ctx->header_oid, "",
|
||||
m_src_image_ctx->md_ctx, m_src_image_ctx->header_oid, true, "",
|
||||
m_last_metadata_key, MAX_METADATA_ITEMS, &m_metadata, ctx);
|
||||
req->send();
|
||||
}
|
||||
|
@ -19,15 +19,21 @@
|
||||
|
||||
namespace librbd {
|
||||
namespace image {
|
||||
namespace {
|
||||
|
||||
static const std::string INTERNAL_KEY_PREFIX{".rbd"};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
using util::create_rados_callback;
|
||||
|
||||
template <typename I>
|
||||
GetMetadataRequest<I>::GetMetadataRequest(
|
||||
IoCtx &io_ctx, const std::string &oid, const std::string& filter,
|
||||
const std::string& last_key, uint32_t max_results,
|
||||
KeyValues* key_values, Context *on_finish)
|
||||
: m_io_ctx(io_ctx), m_oid(oid), m_filter(filter), m_last_key(last_key),
|
||||
IoCtx &io_ctx, const std::string &oid, bool filter_internal,
|
||||
const std::string& filter_key_prefix, const std::string& last_key,
|
||||
uint32_t max_results, KeyValues* key_values, Context *on_finish)
|
||||
: m_io_ctx(io_ctx), m_oid(oid), m_filter_internal(filter_internal),
|
||||
m_filter_key_prefix(filter_key_prefix), m_last_key(last_key),
|
||||
m_max_results(max_results), m_key_values(key_values),
|
||||
m_on_finish(on_finish),
|
||||
m_cct(reinterpret_cast<CephContext*>(m_io_ctx.cct())) {
|
||||
@ -79,7 +85,11 @@ void GetMetadataRequest<I>::handle_metadata_list(int r) {
|
||||
}
|
||||
|
||||
for (auto it = metadata.begin(); it != metadata.end(); ++it) {
|
||||
if (!m_filter.empty() && !boost::starts_with(it->first, m_filter)) {
|
||||
if (m_filter_internal &&
|
||||
boost::starts_with(it->first, INTERNAL_KEY_PREFIX)) {
|
||||
continue;
|
||||
} else if (!m_filter_key_prefix.empty() &&
|
||||
!boost::starts_with(it->first, m_filter_key_prefix)) {
|
||||
continue;
|
||||
}
|
||||
m_key_values->insert({it->first, std::move(it->second)});
|
||||
|
@ -24,17 +24,18 @@ public:
|
||||
typedef std::map<std::string, bufferlist> KeyValues;
|
||||
|
||||
static GetMetadataRequest* create(
|
||||
IoCtx &io_ctx, const std::string &oid, const std::string& filter,
|
||||
const std::string& last_key, uint32_t max_results, KeyValues* key_values,
|
||||
Context *on_finish) {
|
||||
return new GetMetadataRequest(io_ctx, oid, filter, last_key, max_results,
|
||||
IoCtx &io_ctx, const std::string &oid, bool filter_internal,
|
||||
const std::string& filter_key_prefix, const std::string& last_key,
|
||||
uint32_t max_results, KeyValues* key_values, Context *on_finish) {
|
||||
return new GetMetadataRequest(io_ctx, oid, filter_internal,
|
||||
filter_key_prefix, last_key, max_results,
|
||||
key_values, on_finish);
|
||||
}
|
||||
|
||||
GetMetadataRequest(
|
||||
IoCtx &io_ctx, const std::string &oid, const std::string& filter,
|
||||
const std::string& last_key, uint32_t max_results, KeyValues* key_values,
|
||||
Context *on_finish);
|
||||
IoCtx &io_ctx, const std::string &oid, bool filter_internal,
|
||||
const std::string& filter_key_prefix, const std::string& last_key,
|
||||
uint32_t max_results, KeyValues* key_values, Context *on_finish);
|
||||
|
||||
void send();
|
||||
|
||||
@ -56,7 +57,8 @@ private:
|
||||
*/
|
||||
librados::IoCtx m_io_ctx;
|
||||
std::string m_oid;
|
||||
std::string m_filter;
|
||||
bool m_filter_internal;
|
||||
std::string m_filter_key_prefix;
|
||||
std::string m_last_key;
|
||||
uint32_t m_max_results;
|
||||
KeyValues* m_key_values;
|
||||
|
@ -500,7 +500,7 @@ void RefreshRequest<I>::send_v2_get_metadata() {
|
||||
auto ctx = create_context_callback<
|
||||
RefreshRequest<I>, &RefreshRequest<I>::handle_v2_get_metadata>(this);
|
||||
auto req = GetMetadataRequest<I>::create(
|
||||
m_image_ctx.md_ctx, m_image_ctx.header_oid,
|
||||
m_image_ctx.md_ctx, m_image_ctx.header_oid, true,
|
||||
ImageCtx::METADATA_CONF_PREFIX, ImageCtx::METADATA_CONF_PREFIX, 0U,
|
||||
&m_metadata, ctx);
|
||||
req->send();
|
||||
@ -529,7 +529,7 @@ void RefreshRequest<I>::send_v2_get_pool_metadata() {
|
||||
auto ctx = create_context_callback<
|
||||
RefreshRequest<I>, &RefreshRequest<I>::handle_v2_get_pool_metadata>(this);
|
||||
auto req = GetMetadataRequest<I>::create(
|
||||
m_pool_metadata_io_ctx, RBD_INFO, ImageCtx::METADATA_CONF_PREFIX,
|
||||
m_pool_metadata_io_ctx, RBD_INFO, true, ImageCtx::METADATA_CONF_PREFIX,
|
||||
ImageCtx::METADATA_CONF_PREFIX, 0U, &m_metadata, ctx);
|
||||
req->send();
|
||||
}
|
||||
|
@ -1639,7 +1639,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
|
||||
|
||||
C_SaferCond ctx;
|
||||
auto req = image::GetMetadataRequest<>::create(
|
||||
ictx->md_ctx, ictx->header_oid, "", start, max, pairs, &ctx);
|
||||
ictx->md_ctx, ictx->header_oid, false, "", start, max, pairs, &ctx);
|
||||
req->send();
|
||||
|
||||
return ctx.wait();
|
||||
|
174
src/librbd/mirror/snapshot/ImageMeta.cc
Normal file
174
src/librbd/mirror/snapshot/ImageMeta.cc
Normal file
@ -0,0 +1,174 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab
|
||||
|
||||
#include "librbd/mirror/snapshot/ImageMeta.h"
|
||||
#include "common/dout.h"
|
||||
#include "common/errno.h"
|
||||
#include "cls/rbd/cls_rbd_client.h"
|
||||
#include "json_spirit/json_spirit.h"
|
||||
#include "librbd/ImageCtx.h"
|
||||
#include "librbd/Utils.h"
|
||||
#include "librbd/WatchNotifyTypes.h"
|
||||
#include "librbd/mirror/snapshot/Utils.h"
|
||||
#include "librbd/watcher/Notifier.h"
|
||||
|
||||
#define dout_subsys ceph_subsys_rbd
|
||||
|
||||
#undef dout_prefix
|
||||
#define dout_prefix *_dout << "librbd::mirror::snapshot::ImageMeta: " \
|
||||
<< this << " " << __func__ << ": "
|
||||
|
||||
namespace librbd {
|
||||
namespace mirror {
|
||||
namespace snapshot {
|
||||
|
||||
using librbd::util::create_rados_callback;
|
||||
using librbd::mirror::snapshot::util::get_image_meta_key;
|
||||
|
||||
template <typename I>
|
||||
ImageMeta<I>::ImageMeta(I* image_ctx, const std::string& mirror_uuid)
|
||||
: m_image_ctx(image_ctx), m_mirror_uuid(mirror_uuid) {
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void ImageMeta<I>::load(Context* on_finish) {
|
||||
ldout(m_image_ctx->cct, 15) << "oid=" << m_image_ctx->header_oid << ", "
|
||||
<< "key=" << get_image_meta_key(m_mirror_uuid)
|
||||
<< dendl;
|
||||
|
||||
librados::ObjectReadOperation op;
|
||||
cls_client::metadata_get_start(&op, get_image_meta_key(m_mirror_uuid));
|
||||
|
||||
m_out_bl.clear();
|
||||
auto ctx = new LambdaContext([this, on_finish](int r) {
|
||||
handle_load(on_finish, r);
|
||||
});
|
||||
auto aio_comp = create_rados_callback(ctx);
|
||||
int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, aio_comp,
|
||||
&op, &m_out_bl);
|
||||
ceph_assert(r == 0);
|
||||
aio_comp->release();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void ImageMeta<I>::handle_load(Context* on_finish, int r) {
|
||||
ldout(m_image_ctx->cct, 15) << "r=" << r << dendl;
|
||||
|
||||
std::string data;
|
||||
if (r == 0) {
|
||||
auto it = m_out_bl.cbegin();
|
||||
r = cls_client::metadata_get_finish(&it, &data);
|
||||
}
|
||||
|
||||
if (r == -ENOENT) {
|
||||
ldout(m_image_ctx->cct, 15) << "no snapshot-based mirroring image-meta: "
|
||||
<< cpp_strerror(r) << dendl;
|
||||
on_finish->complete(r);
|
||||
return;
|
||||
} else if (r < 0) {
|
||||
lderr(m_image_ctx->cct) << "failed to load snapshot-based mirroring "
|
||||
<< "image-meta: " << cpp_strerror(r) << dendl;
|
||||
on_finish->complete(r);
|
||||
return;
|
||||
}
|
||||
|
||||
bool json_valid = false;
|
||||
json_spirit::mValue json_root;
|
||||
if (json_spirit::read(data, json_root)) {
|
||||
try {
|
||||
auto& json_obj = json_root.get_obj();
|
||||
resync_requested = json_obj["resync_requested"].get_bool();
|
||||
json_valid = true;
|
||||
} catch (std::runtime_error&) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!json_valid) {
|
||||
lderr(m_image_ctx->cct) << "invalid image-meta JSON received" << dendl;
|
||||
on_finish->complete(-EBADMSG);
|
||||
return;
|
||||
}
|
||||
|
||||
on_finish->complete(0);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void ImageMeta<I>::save(Context* on_finish) {
|
||||
ldout(m_image_ctx->cct, 15) << "oid=" << m_image_ctx->header_oid << ", "
|
||||
<< "key=" << get_image_meta_key(m_mirror_uuid)
|
||||
<< dendl;
|
||||
|
||||
// simple implementation for now
|
||||
std::string json = "{\"resync_requested\": " +
|
||||
std::string(resync_requested ? "true" : "false") + "}";
|
||||
|
||||
bufferlist bl;
|
||||
bl.append(json);
|
||||
|
||||
// avoid using built-in metadata_set operation since that would require
|
||||
// opening the non-primary image in read/write mode which isn't supported
|
||||
librados::ObjectWriteOperation op;
|
||||
cls_client::metadata_set(&op, {{get_image_meta_key(m_mirror_uuid), bl}});
|
||||
|
||||
auto ctx = new LambdaContext([this, on_finish](int r) {
|
||||
handle_save(on_finish, r);
|
||||
});
|
||||
auto aio_comp = create_rados_callback(ctx);
|
||||
int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, aio_comp,
|
||||
&op);
|
||||
ceph_assert(r == 0);
|
||||
aio_comp->release();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void ImageMeta<I>::handle_save(Context* on_finish, int r) {
|
||||
ldout(m_image_ctx->cct, 15) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0) {
|
||||
lderr(m_image_ctx->cct) << "failed to save snapshot-based mirroring "
|
||||
<< "image-meta: " << cpp_strerror(r) << dendl;
|
||||
on_finish->complete(r);
|
||||
return;
|
||||
}
|
||||
|
||||
notify_update(on_finish);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void ImageMeta<I>::notify_update(Context* on_finish) {
|
||||
ldout(m_image_ctx->cct, 15) << dendl;
|
||||
|
||||
// directly send header notification on image since you cannot
|
||||
// open a non-primary image read/write and therefore cannot re-use
|
||||
// the ImageWatcher to send the notification
|
||||
bufferlist bl;
|
||||
encode(watch_notify::NotifyMessage(watch_notify::HeaderUpdatePayload()), bl);
|
||||
|
||||
m_out_bl.clear();
|
||||
auto ctx = new LambdaContext([this, on_finish](int r) {
|
||||
handle_notify_update(on_finish, r);
|
||||
});
|
||||
auto aio_comp = create_rados_callback(ctx);
|
||||
int r = m_image_ctx->md_ctx.aio_notify(
|
||||
m_image_ctx->header_oid, aio_comp, bl, watcher::Notifier::NOTIFY_TIMEOUT,
|
||||
&m_out_bl);
|
||||
ceph_assert(r == 0);
|
||||
aio_comp->release();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void ImageMeta<I>::handle_notify_update(Context* on_finish, int r) {
|
||||
ldout(m_image_ctx->cct, 15) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0) {
|
||||
lderr(m_image_ctx->cct) << "failed to notify image update: "
|
||||
<< cpp_strerror(r) << dendl;
|
||||
}
|
||||
on_finish->complete(r);
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace mirror
|
||||
} // namespace librbd
|
||||
|
||||
template class librbd::mirror::snapshot::ImageMeta<librbd::ImageCtx>;
|
78
src/librbd/mirror/snapshot/ImageMeta.h
Normal file
78
src/librbd/mirror/snapshot/ImageMeta.h
Normal file
@ -0,0 +1,78 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab
|
||||
|
||||
#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_IMAGE_META_H
|
||||
#define CEPH_LIBRBD_MIRROR_SNAPSHOT_IMAGE_META_H
|
||||
|
||||
#include "include/rados/librados.hpp"
|
||||
#include <string>
|
||||
|
||||
struct Context;
|
||||
|
||||
namespace librbd {
|
||||
|
||||
struct ImageCtx;
|
||||
|
||||
namespace mirror {
|
||||
namespace snapshot {
|
||||
|
||||
template <typename ImageCtxT>
|
||||
class ImageMeta {
|
||||
public:
|
||||
static ImageMeta* create(ImageCtxT* image_ctx,
|
||||
const std::string& mirror_uuid) {
|
||||
return new ImageMeta(image_ctx, mirror_uuid);
|
||||
}
|
||||
|
||||
ImageMeta(ImageCtxT* image_ctx, const std::string& mirror_uuid);
|
||||
|
||||
void load(Context* on_finish);
|
||||
void save(Context* on_finish);
|
||||
|
||||
bool resync_requested = false;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @verbatim
|
||||
*
|
||||
* <start>
|
||||
* |
|
||||
* v
|
||||
* METADATA_GET
|
||||
* |
|
||||
* v
|
||||
* <idle>
|
||||
* |
|
||||
* v
|
||||
* METADATA_SET
|
||||
* |
|
||||
* v
|
||||
* NOTIFY_UPDATE
|
||||
* |
|
||||
* v
|
||||
* <finish>
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
ImageCtxT* m_image_ctx;
|
||||
std::string m_mirror_uuid;
|
||||
|
||||
bufferlist m_out_bl;
|
||||
|
||||
void handle_load(Context* on_finish, int r);
|
||||
|
||||
void handle_save(Context* on_finish, int r);
|
||||
|
||||
void notify_update(Context* on_finish);
|
||||
void handle_notify_update(Context* on_finish, int r);
|
||||
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace mirror
|
||||
} // namespace librbd
|
||||
|
||||
extern template class librbd::mirror::snapshot::ImageMeta<librbd::ImageCtx>;
|
||||
|
||||
#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_IMAGE_META_H
|
@ -7,6 +7,7 @@
|
||||
#include "cls/rbd/cls_rbd_client.h"
|
||||
#include "librbd/ImageCtx.h"
|
||||
#include "librbd/Utils.h"
|
||||
#include "librbd/image/GetMetadataRequest.h"
|
||||
#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@ -78,19 +79,15 @@ void SetImageStateRequest<I>::handle_get_snap_limit(int r) {
|
||||
template <typename I>
|
||||
void SetImageStateRequest<I>::get_metadata() {
|
||||
CephContext *cct = m_image_ctx->cct;
|
||||
ldout(cct, 20) << "start_key=" << m_last_metadata_key << dendl;
|
||||
ldout(cct, 20) << dendl;
|
||||
|
||||
librados::ObjectReadOperation op;
|
||||
cls_client::metadata_list_start(&op, m_last_metadata_key, MAX_METADATA_ITEMS);
|
||||
|
||||
librados::AioCompletion *comp = create_rados_callback<
|
||||
SetImageStateRequest<I>,
|
||||
&SetImageStateRequest<I>::handle_get_metadata>(this);
|
||||
m_bl.clear();
|
||||
int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op,
|
||||
&m_bl);
|
||||
ceph_assert(r == 0);
|
||||
comp->release();
|
||||
auto ctx = create_context_callback<
|
||||
SetImageStateRequest<I>,
|
||||
&SetImageStateRequest<I>::handle_get_metadata>(this);
|
||||
auto req = image::GetMetadataRequest<I>::create(
|
||||
m_image_ctx->md_ctx, m_image_ctx->header_oid, true, "", "", 0,
|
||||
&m_image_state.metadata, ctx);
|
||||
req->send();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
@ -98,12 +95,6 @@ void SetImageStateRequest<I>::handle_get_metadata(int r) {
|
||||
CephContext *cct = m_image_ctx->cct;
|
||||
ldout(cct, 20) << "r=" << r << dendl;
|
||||
|
||||
std::map<std::string, bufferlist> metadata;
|
||||
if (r == 0) {
|
||||
auto it = m_bl.cbegin();
|
||||
r = cls_client::metadata_list_finish(&it, &metadata);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
lderr(cct) << "failed to retrieve metadata: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
@ -111,16 +102,6 @@ void SetImageStateRequest<I>::handle_get_metadata(int r) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!metadata.empty()) {
|
||||
m_image_state.metadata.insert(metadata.begin(), metadata.end());
|
||||
m_last_metadata_key = metadata.rbegin()->first;
|
||||
if (boost::starts_with(m_last_metadata_key,
|
||||
ImageCtx::METADATA_CONF_PREFIX)) {
|
||||
get_metadata();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_lock image_locker{m_image_ctx->image_lock};
|
||||
|
||||
|
@ -43,8 +43,8 @@ private:
|
||||
* GET_SNAP_LIMIT
|
||||
* |
|
||||
* v
|
||||
* GET_METADATA (repeat until
|
||||
* | all metadata read)
|
||||
* GET_METADATA
|
||||
* |
|
||||
* v
|
||||
* WRITE_IMAGE_STATE
|
||||
* |
|
||||
@ -65,7 +65,6 @@ private:
|
||||
|
||||
bufferlist m_bl;
|
||||
bufferlist m_state_bl;
|
||||
std::string m_last_metadata_key;
|
||||
|
||||
void get_snap_limit();
|
||||
void handle_get_snap_limit(int r);
|
||||
|
@ -48,6 +48,10 @@ bool get_rollback_snap_id(
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::string get_image_meta_key(const std::string& mirror_uuid) {
|
||||
return ".rbd_mirror." + mirror_uuid;
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
bool can_create_primary_snapshot(I *image_ctx, bool demoted, bool force,
|
||||
uint64_t *rollback_snap_id) {
|
||||
|
@ -4,7 +4,9 @@
|
||||
#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_UTILS_H
|
||||
#define CEPH_LIBRBD_MIRROR_SNAPSHOT_UTILS_H
|
||||
|
||||
#include "include/int_types.h"
|
||||
#include "include/stringify.h"
|
||||
#include <string>
|
||||
|
||||
namespace librbd {
|
||||
|
||||
@ -14,6 +16,8 @@ namespace mirror {
|
||||
namespace snapshot {
|
||||
namespace util {
|
||||
|
||||
std::string get_image_meta_key(const std::string& mirror_uuid);
|
||||
|
||||
template <typename ImageCtxT = librbd::ImageCtx>
|
||||
bool can_create_primary_snapshot(ImageCtxT *image_ctx, bool demoted, bool force,
|
||||
uint64_t *rollback_snap_id);
|
||||
|
@ -87,6 +87,7 @@ set(unittest_librbd_srcs
|
||||
managed_lock/test_mock_ReleaseRequest.cc
|
||||
mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc
|
||||
mirror/snapshot/test_mock_CreatePrimaryRequest.cc
|
||||
mirror/snapshot/test_mock_ImageMeta.cc
|
||||
mirror/snapshot/test_mock_PromoteRequest.cc
|
||||
mirror/snapshot/test_mock_UnlinkPeerRequest.cc
|
||||
mirror/snapshot/test_mock_Utils.cc
|
||||
|
@ -33,7 +33,8 @@ struct GetMetadataRequest<MockTestImageCtx> {
|
||||
static GetMetadataRequest* s_instance;
|
||||
static GetMetadataRequest* create(librados::IoCtx&,
|
||||
const std::string& oid,
|
||||
const std::string& filter,
|
||||
bool filter_internal,
|
||||
const std::string& filter_key_prefix,
|
||||
const std::string& last_key,
|
||||
uint32_t max_results,
|
||||
std::map<std::string, bufferlist>* pairs,
|
||||
|
@ -46,13 +46,14 @@ struct GetMetadataRequest<MockRefreshImageCtx> {
|
||||
static GetMetadataRequest* s_instance;
|
||||
static GetMetadataRequest* create(librados::IoCtx&,
|
||||
const std::string& oid,
|
||||
const std::string& filter,
|
||||
bool filter_internal,
|
||||
const std::string& filter_key_prefix,
|
||||
const std::string& last_key,
|
||||
uint32_t max_results,
|
||||
std::map<std::string, bufferlist>* pairs,
|
||||
Context* on_finish) {
|
||||
ceph_assert(s_instance != nullptr);
|
||||
EXPECT_EQ("conf_", filter);
|
||||
EXPECT_EQ("conf_", filter_key_prefix);
|
||||
EXPECT_EQ("conf_", last_key);
|
||||
s_instance->oid = oid;
|
||||
s_instance->pairs = pairs;
|
||||
|
159
src/test/librbd/mirror/snapshot/test_mock_ImageMeta.cc
Normal file
159
src/test/librbd/mirror/snapshot/test_mock_ImageMeta.cc
Normal file
@ -0,0 +1,159 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab
|
||||
|
||||
#include "test/librbd/test_mock_fixture.h"
|
||||
#include "test/librbd/test_support.h"
|
||||
#include "test/librbd/mock/MockImageCtx.h"
|
||||
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
|
||||
#include "librbd/ImageState.h"
|
||||
#include "librbd/mirror/snapshot/ImageMeta.h"
|
||||
#include "librbd/mirror/snapshot/Utils.h"
|
||||
|
||||
namespace librbd {
|
||||
namespace {
|
||||
|
||||
struct MockTestImageCtx : public MockImageCtx {
|
||||
MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
} // namespace librbd
|
||||
|
||||
#include "librbd/mirror/snapshot/ImageMeta.cc"
|
||||
|
||||
namespace librbd {
|
||||
namespace mirror {
|
||||
namespace snapshot {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::Return;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::WithArg;
|
||||
|
||||
class TestMockMirrorSnapshotImageMeta : public TestMockFixture {
|
||||
public:
|
||||
typedef ImageMeta<MockTestImageCtx> MockImageMeta;
|
||||
|
||||
void expect_metadata_get(MockTestImageCtx& mock_image_ctx,
|
||||
const std::string& mirror_uuid,
|
||||
const std::string& value, int r) {
|
||||
bufferlist in_bl;
|
||||
ceph::encode(util::get_image_meta_key(mirror_uuid), in_bl);
|
||||
|
||||
bufferlist out_bl;
|
||||
ceph::encode(value, out_bl);
|
||||
|
||||
EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
|
||||
exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
|
||||
StrEq("metadata_get"), ContentsEqual(in_bl), _, _))
|
||||
.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(out_bl)),
|
||||
Return(r)));
|
||||
}
|
||||
|
||||
void expect_metadata_set(MockTestImageCtx& mock_image_ctx,
|
||||
const std::string& mirror_uuid,
|
||||
const std::string& value, int r) {
|
||||
bufferlist value_bl;
|
||||
value_bl.append(value);
|
||||
|
||||
bufferlist in_bl;
|
||||
ceph::encode(
|
||||
std::map<std::string, bufferlist>{
|
||||
{util::get_image_meta_key(mirror_uuid), value_bl}},
|
||||
in_bl);
|
||||
|
||||
EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
|
||||
exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
|
||||
StrEq("metadata_set"), ContentsEqual(in_bl), _, _))
|
||||
.WillOnce(Return(r));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestMockMirrorSnapshotImageMeta, Load) {
|
||||
librbd::ImageCtx* image_ctx;
|
||||
ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
|
||||
MockTestImageCtx mock_image_ctx(*image_ctx);
|
||||
|
||||
InSequence seq;
|
||||
expect_metadata_get(mock_image_ctx, "mirror uuid",
|
||||
"{\"resync_requested\": true}", 0);
|
||||
|
||||
MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
|
||||
C_SaferCond ctx;
|
||||
mock_image_meta.load(&ctx);
|
||||
ASSERT_EQ(0, ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockMirrorSnapshotImageMeta, LoadError) {
|
||||
librbd::ImageCtx* image_ctx;
|
||||
ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
|
||||
MockTestImageCtx mock_image_ctx(*image_ctx);
|
||||
|
||||
InSequence seq;
|
||||
expect_metadata_get(mock_image_ctx, "mirror uuid",
|
||||
"{\"resync_requested\": true}", -EINVAL);
|
||||
|
||||
MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
|
||||
C_SaferCond ctx;
|
||||
mock_image_meta.load(&ctx);
|
||||
ASSERT_EQ(-EINVAL, ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockMirrorSnapshotImageMeta, LoadCorrupt) {
|
||||
librbd::ImageCtx* image_ctx;
|
||||
ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
|
||||
MockTestImageCtx mock_image_ctx(*image_ctx);
|
||||
|
||||
InSequence seq;
|
||||
expect_metadata_get(mock_image_ctx, "mirror uuid",
|
||||
"\"resync_requested\": true}", 0);
|
||||
|
||||
MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
|
||||
C_SaferCond ctx;
|
||||
mock_image_meta.load(&ctx);
|
||||
ASSERT_EQ(-EBADMSG, ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockMirrorSnapshotImageMeta, Save) {
|
||||
librbd::ImageCtx* image_ctx;
|
||||
ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
|
||||
MockTestImageCtx mock_image_ctx(*image_ctx);
|
||||
|
||||
InSequence seq;
|
||||
expect_metadata_set(mock_image_ctx, "mirror uuid",
|
||||
"{\"resync_requested\": true}", 0);
|
||||
|
||||
MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
|
||||
mock_image_meta.resync_requested = true;
|
||||
|
||||
C_SaferCond ctx;
|
||||
mock_image_meta.save(&ctx);
|
||||
ASSERT_EQ(0, ctx.wait());
|
||||
|
||||
// should have sent image-update notification
|
||||
ASSERT_TRUE(image_ctx->state->is_refresh_required());
|
||||
}
|
||||
|
||||
TEST_F(TestMockMirrorSnapshotImageMeta, SaveError) {
|
||||
librbd::ImageCtx* image_ctx;
|
||||
ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
|
||||
MockTestImageCtx mock_image_ctx(*image_ctx);
|
||||
|
||||
InSequence seq;
|
||||
expect_metadata_set(mock_image_ctx, "mirror uuid",
|
||||
"{\"resync_requested\": false}", -EINVAL);
|
||||
|
||||
MockImageMeta mock_image_meta(&mock_image_ctx, "mirror uuid");
|
||||
|
||||
C_SaferCond ctx;
|
||||
mock_image_meta.save(&ctx);
|
||||
ASSERT_EQ(-EINVAL, ctx.wait());
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace mirror
|
||||
} // namespace librbd
|
@ -32,7 +32,8 @@ struct GetMetadataRequest<MockTestImageCtx> {
|
||||
static GetMetadataRequest* s_instance;
|
||||
static GetMetadataRequest* create(librados::IoCtx& io_ctx,
|
||||
const std::string& oid,
|
||||
const std::string& filter,
|
||||
bool filter_internal,
|
||||
const std::string& filter_key_prefix,
|
||||
const std::string& last_key,
|
||||
size_t max_results,
|
||||
std::map<std::string, bufferlist>* pairs,
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "librbd/deep_copy/SnapshotCopyRequest.h"
|
||||
#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
|
||||
#include "librbd/mirror/snapshot/GetImageStateRequest.h"
|
||||
#include "librbd/mirror/snapshot/ImageMeta.h"
|
||||
#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
|
||||
#include "tools/rbd_mirror/Threads.h"
|
||||
#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
|
||||
@ -173,6 +174,13 @@ struct GetImageStateRequest<MockTestImageCtx> {
|
||||
MOCK_METHOD0(send, void());
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ImageMeta<MockTestImageCtx> {
|
||||
MOCK_METHOD1(load, void(Context*));
|
||||
|
||||
bool resync_requested = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct UnlinkPeerRequest<MockTestImageCtx> {
|
||||
uint64_t snap_id;
|
||||
@ -296,15 +304,21 @@ struct ApplyImageStateRequest<librbd::MockTestImageCtx> {
|
||||
template<>
|
||||
struct StateBuilder<librbd::MockTestImageCtx> {
|
||||
StateBuilder(librbd::MockTestImageCtx& local_image_ctx,
|
||||
librbd::MockTestImageCtx& remote_image_ctx)
|
||||
librbd::MockTestImageCtx& remote_image_ctx,
|
||||
librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx>&
|
||||
local_image_meta)
|
||||
: local_image_ctx(&local_image_ctx),
|
||||
remote_image_ctx(&remote_image_ctx) {
|
||||
remote_image_ctx(&remote_image_ctx),
|
||||
local_image_meta(&local_image_meta) {
|
||||
}
|
||||
|
||||
librbd::MockTestImageCtx* local_image_ctx;
|
||||
librbd::MockTestImageCtx* remote_image_ctx;
|
||||
|
||||
std::string remote_mirror_uuid = "remote mirror uuid";
|
||||
|
||||
librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx>*
|
||||
local_image_meta = nullptr;
|
||||
};
|
||||
|
||||
ApplyImageStateRequest<librbd::MockTestImageCtx>* ApplyImageStateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
|
||||
@ -341,6 +355,7 @@ public:
|
||||
typedef librbd::deep_copy::SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest;
|
||||
typedef librbd::mirror::snapshot::CreateNonPrimaryRequest<librbd::MockTestImageCtx> MockCreateNonPrimaryRequest;
|
||||
typedef librbd::mirror::snapshot::GetImageStateRequest<librbd::MockTestImageCtx> MockGetImageStateRequest;
|
||||
typedef librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx> MockImageMeta;
|
||||
typedef librbd::mirror::snapshot::UnlinkPeerRequest<librbd::MockTestImageCtx> MockUnlinkPeerRequest;
|
||||
|
||||
void SetUp() override {
|
||||
@ -399,6 +414,15 @@ public:
|
||||
})));
|
||||
}
|
||||
|
||||
void expect_load_image_meta(MockImageMeta& mock_image_meta,
|
||||
bool resync_requested, int r) {
|
||||
EXPECT_CALL(mock_image_meta, load(_))
|
||||
.WillOnce(Invoke([this, &mock_image_meta, resync_requested, r](Context* ctx) {
|
||||
mock_image_meta.resync_requested = resync_requested;
|
||||
m_threads->work_queue->queue(ctx, r);
|
||||
}));
|
||||
}
|
||||
|
||||
void expect_is_refresh_required(librbd::MockTestImageCtx& mock_image_ctx,
|
||||
bool is_required) {
|
||||
EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
|
||||
@ -556,9 +580,13 @@ public:
|
||||
librbd::MockTestImageCtx& mock_local_image_ctx,
|
||||
librbd::MockTestImageCtx& mock_remote_image_ctx,
|
||||
MockReplayerListener& mock_replayer_listener,
|
||||
MockImageMeta& mock_image_meta,
|
||||
librbd::UpdateWatchCtx** update_watch_ctx) {
|
||||
expect_register_update_watcher(mock_remote_image_ctx, update_watch_ctx, 123,
|
||||
expect_register_update_watcher(mock_local_image_ctx, update_watch_ctx, 123,
|
||||
0);
|
||||
expect_register_update_watcher(mock_remote_image_ctx, update_watch_ctx, 234,
|
||||
0);
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
|
||||
@ -574,8 +602,10 @@ public:
|
||||
|
||||
int shut_down_entry_replayer(MockReplayer& mock_replayer,
|
||||
MockThreads& mock_threads,
|
||||
librbd::MockTestImageCtx& mock_local_image_ctx,
|
||||
librbd::MockTestImageCtx& mock_remote_image_ctx) {
|
||||
expect_unregister_update_watcher(mock_remote_image_ctx, 123, 0);
|
||||
expect_unregister_update_watcher(mock_remote_image_ctx, 234, 0);
|
||||
expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
|
||||
|
||||
C_SaferCond shutdown_ctx;
|
||||
mock_replayer.shut_down(&shutdown_ctx);
|
||||
@ -605,8 +635,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InitShutDown) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -619,8 +651,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InitShutDown) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -654,8 +688,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -666,10 +702,13 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
|
||||
librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
|
||||
|
||||
// init
|
||||
expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 123,
|
||||
expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
|
||||
0);
|
||||
expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 234,
|
||||
0);
|
||||
|
||||
// sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -691,6 +730,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
|
||||
expect_notify_update(mock_local_image_ctx);
|
||||
|
||||
// sync snap4
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, true);
|
||||
expect_refresh(
|
||||
mock_local_image_ctx, {
|
||||
@ -717,6 +757,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
|
||||
0);
|
||||
|
||||
// idle
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, true);
|
||||
expect_refresh(
|
||||
mock_local_image_ctx, {
|
||||
@ -741,6 +782,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
|
||||
|
||||
// shut down
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -756,8 +798,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSync) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -770,6 +814,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSync) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject a incomplete sync snapshot
|
||||
@ -785,6 +830,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSync) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// re-sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockGetImageStateRequest mock_get_image_state_request;
|
||||
@ -800,6 +846,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSync) {
|
||||
expect_notify_update(mock_local_image_ctx);
|
||||
|
||||
// idle
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, true);
|
||||
expect_refresh(
|
||||
mock_local_image_ctx, {
|
||||
@ -817,6 +864,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSync) {
|
||||
ASSERT_EQ(0, wait_for_notification(2));
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -832,8 +880,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteImageDemoted) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -846,6 +896,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteImageDemoted) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject a demotion snapshot
|
||||
@ -856,6 +907,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteImageDemoted) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -877,6 +929,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteImageDemoted) {
|
||||
expect_notify_update(mock_local_image_ctx);
|
||||
|
||||
// idle
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, true);
|
||||
expect_refresh(
|
||||
mock_local_image_ctx, {
|
||||
@ -895,6 +948,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteImageDemoted) {
|
||||
ASSERT_FALSE(mock_replayer.is_replaying());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -910,8 +964,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, LocalImagePromoted) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -924,6 +980,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, LocalImagePromoted) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject a promotion snapshot
|
||||
@ -934,6 +991,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, LocalImagePromoted) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// idle
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
|
||||
@ -945,40 +1003,11 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, LocalImagePromoted) {
|
||||
ASSERT_FALSE(mock_replayer.is_replaying());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, RegisterUpdateWatcherError) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
|
||||
MockThreads mock_threads(m_threads);
|
||||
expect_work_queue_repeatedly(mock_threads);
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
MockReplayerListener mock_replayer_listener;
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
m_pool_meta_cache.set_remote_pool_meta(
|
||||
m_remote_io_ctx.get_id(),
|
||||
{"remote mirror uuid", "remote mirror peer uuid"});
|
||||
|
||||
// init
|
||||
librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
|
||||
expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 123,
|
||||
-EINVAL);
|
||||
|
||||
// fire init
|
||||
C_SaferCond init_ctx;
|
||||
mock_replayer.init(&init_ctx);
|
||||
ASSERT_EQ(-EINVAL, init_ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, UnregisterUpdateWatcherError) {
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, ResyncRequested) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
|
||||
@ -990,8 +1019,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnregisterUpdateWatcherError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1004,17 +1035,221 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnregisterUpdateWatcherError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// idle
|
||||
expect_load_image_meta(mock_image_meta, true, 0);
|
||||
|
||||
// wake-up replayer
|
||||
update_watch_ctx->handle_notify();
|
||||
|
||||
// wait for sync to complete and expect replay complete
|
||||
ASSERT_EQ(0, wait_for_notification(1));
|
||||
ASSERT_FALSE(mock_replayer.is_replaying());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, RegisterLocalUpdateWatcherError) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
|
||||
MockThreads mock_threads(m_threads);
|
||||
expect_work_queue_repeatedly(mock_threads);
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayerListener mock_replayer_listener;
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
m_pool_meta_cache.set_remote_pool_meta(
|
||||
m_remote_io_ctx.get_id(),
|
||||
{"remote mirror uuid", "remote mirror peer uuid"});
|
||||
|
||||
// init
|
||||
librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
|
||||
expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
|
||||
-EINVAL);
|
||||
|
||||
// fire init
|
||||
C_SaferCond init_ctx;
|
||||
mock_replayer.init(&init_ctx);
|
||||
ASSERT_EQ(-EINVAL, init_ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, RegisterRemoteUpdateWatcherError) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
|
||||
MockThreads mock_threads(m_threads);
|
||||
expect_work_queue_repeatedly(mock_threads);
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayerListener mock_replayer_listener;
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
m_pool_meta_cache.set_remote_pool_meta(
|
||||
m_remote_io_ctx.get_id(),
|
||||
{"remote mirror uuid", "remote mirror peer uuid"});
|
||||
|
||||
// init
|
||||
librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
|
||||
expect_register_update_watcher(mock_local_image_ctx, &update_watch_ctx, 123,
|
||||
0);
|
||||
expect_register_update_watcher(mock_remote_image_ctx, &update_watch_ctx, 234,
|
||||
-EINVAL);
|
||||
|
||||
expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
|
||||
|
||||
// fire init
|
||||
C_SaferCond init_ctx;
|
||||
mock_replayer.init(&init_ctx);
|
||||
ASSERT_EQ(-EINVAL, init_ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, UnregisterRemoteUpdateWatcherError) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
|
||||
MockThreads mock_threads(m_threads);
|
||||
expect_work_queue_repeatedly(mock_threads);
|
||||
|
||||
MockReplayerListener mock_replayer_listener;
|
||||
expect_notification(mock_threads, mock_replayer_listener);
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
m_pool_meta_cache.set_remote_pool_meta(
|
||||
m_remote_io_ctx.get_id(),
|
||||
{"remote mirror uuid", "remote mirror peer uuid"});
|
||||
|
||||
librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
|
||||
ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
|
||||
// shut down
|
||||
expect_unregister_update_watcher(mock_remote_image_ctx, 123, -EINVAL);
|
||||
expect_unregister_update_watcher(mock_remote_image_ctx, 234, -EINVAL);
|
||||
expect_unregister_update_watcher(mock_local_image_ctx, 123, 0);
|
||||
|
||||
C_SaferCond shutdown_ctx;
|
||||
mock_replayer.shut_down(&shutdown_ctx);
|
||||
ASSERT_EQ(-EINVAL, shutdown_ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, UnregisterLocalUpdateWatcherError) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
|
||||
MockThreads mock_threads(m_threads);
|
||||
expect_work_queue_repeatedly(mock_threads);
|
||||
|
||||
MockReplayerListener mock_replayer_listener;
|
||||
expect_notification(mock_threads, mock_replayer_listener);
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
m_pool_meta_cache.set_remote_pool_meta(
|
||||
m_remote_io_ctx.get_id(),
|
||||
{"remote mirror uuid", "remote mirror peer uuid"});
|
||||
|
||||
librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
|
||||
ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
|
||||
// shut down
|
||||
expect_unregister_update_watcher(mock_remote_image_ctx, 234, 0);
|
||||
expect_unregister_update_watcher(mock_local_image_ctx, 123, -EINVAL);
|
||||
|
||||
C_SaferCond shutdown_ctx;
|
||||
mock_replayer.shut_down(&shutdown_ctx);
|
||||
ASSERT_EQ(-EINVAL, shutdown_ctx.wait());
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, LoadImageMetaError) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
|
||||
MockThreads mock_threads(m_threads);
|
||||
expect_work_queue_repeatedly(mock_threads);
|
||||
|
||||
MockReplayerListener mock_replayer_listener;
|
||||
expect_notification(mock_threads, mock_replayer_listener);
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
m_pool_meta_cache.set_remote_pool_meta(
|
||||
m_remote_io_ctx.get_id(),
|
||||
{"remote mirror uuid", "remote mirror peer uuid"});
|
||||
|
||||
librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
|
||||
ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// sync
|
||||
expect_load_image_meta(mock_image_meta, false, -EINVAL);
|
||||
|
||||
// wake-up replayer
|
||||
update_watch_ctx->handle_notify();
|
||||
|
||||
// wait for sync to complete and expect replay complete
|
||||
ASSERT_EQ(0, wait_for_notification(1));
|
||||
ASSERT_FALSE(mock_replayer.is_replaying());
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshLocalImageError) {
|
||||
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
|
||||
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
|
||||
@ -1027,8 +1262,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshLocalImageError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1041,9 +1278,11 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshLocalImageError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// sync
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, true);
|
||||
expect_refresh(mock_local_image_ctx, {}, -EINVAL);
|
||||
|
||||
@ -1056,6 +1295,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshLocalImageError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -1071,8 +1311,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshRemoteImageError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1085,9 +1327,11 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshRemoteImageError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// sync
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, true);
|
||||
expect_refresh(mock_remote_image_ctx, {}, -EINVAL);
|
||||
@ -1101,6 +1345,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshRemoteImageError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -1116,8 +1361,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopySnapshotsError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1130,6 +1377,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopySnapshotsError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject snapshot
|
||||
@ -1140,6 +1388,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopySnapshotsError) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -1155,6 +1404,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopySnapshotsError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -1170,8 +1420,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, GetImageStateError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1184,6 +1436,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, GetImageStateError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject snapshot
|
||||
@ -1194,6 +1447,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, GetImageStateError) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -1211,6 +1465,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, GetImageStateError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -1226,8 +1481,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CreateNonPrimarySnapshotError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1240,6 +1497,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CreateNonPrimarySnapshotError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject snapshot
|
||||
@ -1250,6 +1508,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CreateNonPrimarySnapshotError) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -1271,6 +1530,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CreateNonPrimarySnapshotError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -1286,8 +1546,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopyImageError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1300,6 +1562,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopyImageError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject snapshot
|
||||
@ -1310,6 +1573,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopyImageError) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -1334,6 +1598,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, CopyImageError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -1349,8 +1614,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UpdateNonPrimarySnapshotError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1363,6 +1630,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UpdateNonPrimarySnapshotError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject snapshot
|
||||
@ -1373,6 +1641,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UpdateNonPrimarySnapshotError) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// sync snap1
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -1401,6 +1670,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UpdateNonPrimarySnapshotError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
@ -1416,8 +1686,10 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkPeerError) {
|
||||
|
||||
InSequence seq;
|
||||
|
||||
MockImageMeta mock_image_meta;
|
||||
MockStateBuilder mock_state_builder(mock_local_image_ctx,
|
||||
mock_remote_image_ctx);
|
||||
mock_remote_image_ctx,
|
||||
mock_image_meta);
|
||||
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
|
||||
&m_pool_meta_cache, &mock_state_builder,
|
||||
&mock_replayer_listener};
|
||||
@ -1430,6 +1702,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkPeerError) {
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx,
|
||||
mock_replayer_listener,
|
||||
mock_image_meta,
|
||||
&update_watch_ctx));
|
||||
|
||||
// inject snapshot
|
||||
@ -1449,6 +1722,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkPeerError) {
|
||||
0, {}, 0, 0, {}}}};
|
||||
|
||||
// sync snap2
|
||||
expect_load_image_meta(mock_image_meta, false, 0);
|
||||
expect_is_refresh_required(mock_local_image_ctx, false);
|
||||
expect_is_refresh_required(mock_remote_image_ctx, false);
|
||||
MockSnapshotCopyRequest mock_snapshot_copy_request;
|
||||
@ -1481,6 +1755,7 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkPeerError) {
|
||||
ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
|
||||
|
||||
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
|
||||
mock_local_image_ctx,
|
||||
mock_remote_image_ctx));
|
||||
}
|
||||
|
||||
|
@ -122,9 +122,14 @@ public:
|
||||
features |= RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_JOURNALING;
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_remote_ioctx,
|
||||
RBD_MIRROR_MODE_POOL));
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_local_ioctx,
|
||||
RBD_MIRROR_MODE_POOL));
|
||||
} else {
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_remote_ioctx,
|
||||
RBD_MIRROR_MODE_IMAGE));
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_local_ioctx,
|
||||
RBD_MIRROR_MODE_IMAGE));
|
||||
|
||||
|
||||
uuid_d uuid_gen;
|
||||
uuid_gen.generate_random();
|
||||
@ -139,6 +144,11 @@ public:
|
||||
m_remote_ioctx.get_id(), {m_remote_mirror_uuid, remote_peer_uuid});
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::uuid_get(m_remote_ioctx,
|
||||
&m_remote_mirror_uuid));
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::uuid_get(m_local_ioctx,
|
||||
&m_local_mirror_uuid));
|
||||
|
||||
m_image_name = get_temp_image_name();
|
||||
int order = 0;
|
||||
EXPECT_EQ(0, librbd::create(m_remote_ioctx, m_image_name.c_str(), 1 << 22,
|
||||
@ -212,6 +222,10 @@ public:
|
||||
m_replayer->start(&cond);
|
||||
ASSERT_EQ(0, cond.wait());
|
||||
|
||||
create_watch_ctx();
|
||||
}
|
||||
|
||||
void create_watch_ctx() {
|
||||
std::string oid;
|
||||
if (MIRROR_IMAGE_MODE == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
|
||||
oid = ::journal::Journaler::header_oid(m_remote_image_id);
|
||||
@ -220,12 +234,9 @@ public:
|
||||
}
|
||||
|
||||
ASSERT_EQ(0U, m_watch_handle);
|
||||
create_watch_ctx(oid);
|
||||
ASSERT_EQ(0, m_remote_ioctx.watch2(oid, &m_watch_handle, m_watch_ctx));
|
||||
}
|
||||
|
||||
void create_watch_ctx(const std::string& oid) {
|
||||
ASSERT_TRUE(m_watch_ctx == nullptr);
|
||||
m_watch_ctx = new C_WatchCtx(this, oid);
|
||||
ASSERT_EQ(0, m_remote_ioctx.watch2(oid, &m_watch_handle, m_watch_ctx));
|
||||
}
|
||||
|
||||
void unwatch() {
|
||||
@ -824,9 +835,8 @@ TEST_F(TestImageReplayerJournal, NextTag)
|
||||
this->stop();
|
||||
}
|
||||
|
||||
TEST_F(TestImageReplayerJournal, Resync)
|
||||
TYPED_TEST(TestImageReplayer, Resync)
|
||||
{
|
||||
// TODO add support to snapshot-based mirroring
|
||||
this->bootstrap();
|
||||
|
||||
librbd::ImageCtx *ictx;
|
||||
@ -852,7 +862,7 @@ TEST_F(TestImageReplayerJournal, Resync)
|
||||
this->close_image(ictx);
|
||||
|
||||
this->open_local_image(&ictx);
|
||||
librbd::Journal<>::request_resync(ictx);
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::image_resync(ictx));
|
||||
this->close_image(ictx);
|
||||
|
||||
this->wait_for_stopped();
|
||||
@ -874,10 +884,8 @@ TEST_F(TestImageReplayerJournal, Resync)
|
||||
this->stop();
|
||||
}
|
||||
|
||||
TEST_F(TestImageReplayerJournal, Resync_While_Stop)
|
||||
TYPED_TEST(TestImageReplayer, Resync_While_Stop)
|
||||
{
|
||||
// TODO add support to snapshot-based mirroring
|
||||
|
||||
this->bootstrap();
|
||||
|
||||
this->start();
|
||||
@ -908,7 +916,7 @@ TEST_F(TestImageReplayerJournal, Resync_While_Stop)
|
||||
ASSERT_EQ(0, cond.wait());
|
||||
|
||||
this->open_local_image(&ictx);
|
||||
librbd::Journal<>::request_resync(ictx);
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::image_resync(ictx));
|
||||
this->close_image(ictx);
|
||||
|
||||
C_SaferCond cond2;
|
||||
@ -935,15 +943,13 @@ TEST_F(TestImageReplayerJournal, Resync_While_Stop)
|
||||
this->stop();
|
||||
}
|
||||
|
||||
TEST_F(TestImageReplayerJournal, Resync_StartInterrupted)
|
||||
TYPED_TEST(TestImageReplayer, Resync_StartInterrupted)
|
||||
{
|
||||
// TODO add support to snapshot-based mirroring
|
||||
|
||||
this->bootstrap();
|
||||
|
||||
librbd::ImageCtx *ictx;
|
||||
this->open_local_image(&ictx);
|
||||
librbd::Journal<>::request_resync(ictx);
|
||||
EXPECT_EQ(0, librbd::api::Mirror<>::image_resync(ictx));
|
||||
this->close_image(ictx);
|
||||
|
||||
C_SaferCond cond;
|
||||
@ -956,11 +962,7 @@ TEST_F(TestImageReplayerJournal, Resync_StartInterrupted)
|
||||
this->m_replayer->start(&cond2);
|
||||
ASSERT_EQ(0, cond2.wait());
|
||||
|
||||
ASSERT_EQ(0U, this->m_watch_handle);
|
||||
std::string oid = ::journal::Journaler::header_oid(this->m_remote_image_id);
|
||||
this->create_watch_ctx(oid);
|
||||
ASSERT_EQ(0, this->m_remote_ioctx.watch2(oid, &this->m_watch_handle,
|
||||
this->m_watch_ctx));
|
||||
this->create_watch_ctx();
|
||||
|
||||
ASSERT_TRUE(this->m_replayer->is_replaying());
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "librbd/Utils.h"
|
||||
#include "librbd/image/GetMetadataRequest.h"
|
||||
#include "tools/rbd_mirror/image_replayer/snapshot/Utils.h"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#define dout_context g_ceph_context
|
||||
#define dout_subsys ceph_subsys_rbd_mirror
|
||||
@ -146,7 +147,7 @@ void ApplyImageStateRequest<I>::get_image_meta() {
|
||||
ApplyImageStateRequest<I>,
|
||||
&ApplyImageStateRequest<I>::handle_get_image_meta>(this);
|
||||
auto req = librbd::image::GetMetadataRequest<I>::create(
|
||||
m_local_image_ctx->md_ctx, m_local_image_ctx->header_oid, "", "", 0U,
|
||||
m_local_image_ctx->md_ctx, m_local_image_ctx->header_oid, true, "", "", 0U,
|
||||
&m_metadata, ctx);
|
||||
req->send();
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common/errno.h"
|
||||
#include "librbd/ImageCtx.h"
|
||||
#include "librbd/Utils.h"
|
||||
#include "librbd/mirror/snapshot/ImageMeta.h"
|
||||
#include "tools/rbd_mirror/ProgressContext.h"
|
||||
#include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
|
||||
|
||||
@ -22,12 +23,42 @@ namespace mirror {
|
||||
namespace image_replayer {
|
||||
namespace snapshot {
|
||||
|
||||
using librbd::util::create_context_callback;
|
||||
|
||||
template <typename I>
|
||||
void PrepareReplayRequest<I>::send() {
|
||||
// TODO
|
||||
*m_resync_requested = false;
|
||||
*m_syncing = false;
|
||||
|
||||
load_local_image_meta();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void PrepareReplayRequest<I>::load_local_image_meta() {
|
||||
dout(15) << dendl;
|
||||
|
||||
ceph_assert(m_state_builder->local_image_meta == nullptr);
|
||||
m_state_builder->local_image_meta =
|
||||
librbd::mirror::snapshot::ImageMeta<I>::create(
|
||||
m_state_builder->local_image_ctx, m_local_mirror_uuid);
|
||||
|
||||
auto ctx = create_context_callback<
|
||||
PrepareReplayRequest<I>,
|
||||
&PrepareReplayRequest<I>::handle_load_local_image_meta>(this);
|
||||
m_state_builder->local_image_meta->load(ctx);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void PrepareReplayRequest<I>::handle_load_local_image_meta(int r) {
|
||||
dout(15) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0 && r != -ENOENT) {
|
||||
derr << "failed to load local image-meta: " << cpp_strerror(r) << dendl;
|
||||
finish(r);
|
||||
return;
|
||||
}
|
||||
|
||||
*m_resync_requested = m_state_builder->local_image_meta->resync_requested;
|
||||
finish(0);
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,9 @@ private:
|
||||
* <start>
|
||||
* |
|
||||
* v
|
||||
* LOAD_LOCAL_IMAGE_META
|
||||
* |
|
||||
* v
|
||||
* <finish>
|
||||
*
|
||||
* @endverbatim
|
||||
@ -77,6 +80,10 @@ private:
|
||||
StateBuilder<ImageCtxT>* m_state_builder;
|
||||
bool* m_resync_requested;
|
||||
bool* m_syncing;
|
||||
|
||||
void load_local_image_meta();
|
||||
void handle_load_local_image_meta(int r);
|
||||
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "librbd/deep_copy/SnapshotCopyRequest.h"
|
||||
#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
|
||||
#include "librbd/mirror/snapshot/GetImageStateRequest.h"
|
||||
#include "librbd/mirror/snapshot/ImageMeta.h"
|
||||
#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
|
||||
#include "tools/rbd_mirror/PoolMetaCache.h"
|
||||
#include "tools/rbd_mirror/Threads.h"
|
||||
@ -73,7 +74,7 @@ struct Replayer<I>::C_UpdateWatchCtx : public librbd::UpdateWatchCtx {
|
||||
}
|
||||
|
||||
void handle_notify() override {
|
||||
replayer->handle_remote_image_update_notify();
|
||||
replayer->handle_image_update_notify();
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,7 +154,7 @@ void Replayer<I>::init(Context* on_finish) {
|
||||
ceph_assert(m_on_init_shutdown == nullptr);
|
||||
m_on_init_shutdown = on_finish;
|
||||
|
||||
register_update_watcher();
|
||||
register_local_update_watcher();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
@ -178,7 +179,7 @@ void Replayer<I>::shut_down(Context* on_finish) {
|
||||
}
|
||||
locker.unlock();
|
||||
|
||||
unregister_update_watcher();
|
||||
unregister_remote_update_watcher();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
@ -274,6 +275,37 @@ bool Replayer<I>::get_replay_status(std::string* description,
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::load_local_image_meta() {
|
||||
dout(10) << dendl;
|
||||
|
||||
ceph_assert(m_state_builder->local_image_meta != nullptr);
|
||||
auto ctx = create_context_callback<
|
||||
Replayer<I>, &Replayer<I>::handle_load_local_image_meta>(this);
|
||||
m_state_builder->local_image_meta->load(ctx);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::handle_load_local_image_meta(int r) {
|
||||
dout(10) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0 && r != -ENOENT) {
|
||||
derr << "failed to load local image-meta: " << cpp_strerror(r) << dendl;
|
||||
handle_replay_complete(r, "failed to load local image-meta");
|
||||
return;
|
||||
}
|
||||
|
||||
if (r >= 0 && m_state_builder->local_image_meta->resync_requested) {
|
||||
m_resync_requested = true;
|
||||
|
||||
dout(10) << "local image resync requested" << dendl;
|
||||
handle_replay_complete(0, "resync requested");
|
||||
return;
|
||||
}
|
||||
|
||||
refresh_local_image();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::refresh_local_image() {
|
||||
if (!m_state_builder->local_image_ctx->state->is_refresh_required()) {
|
||||
@ -429,7 +461,7 @@ void Replayer<I>::scan_remote_mirror_snapshots(
|
||||
dout(10) << dendl;
|
||||
|
||||
// reset state in case new snapshot is added while we are scanning
|
||||
m_remote_image_updated = false;
|
||||
m_image_updated = false;
|
||||
|
||||
bool remote_demoted = false;
|
||||
auto remote_image_ctx = m_state_builder->remote_image_ctx;
|
||||
@ -544,14 +576,14 @@ void Replayer<I>::scan_remote_mirror_snapshots(
|
||||
}
|
||||
}
|
||||
|
||||
if (m_remote_image_updated) {
|
||||
if (m_image_updated) {
|
||||
// received update notification while scanning image, restart ...
|
||||
m_remote_image_updated = false;
|
||||
m_image_updated = false;
|
||||
locker->unlock();
|
||||
|
||||
dout(10) << "restarting snapshot scan due to remote update notification"
|
||||
<< dendl;
|
||||
refresh_local_image();
|
||||
load_local_image_meta();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -866,7 +898,7 @@ void Replayer<I>::unlink_peer() {
|
||||
notify_status_updated();
|
||||
}
|
||||
|
||||
refresh_local_image();
|
||||
load_local_image_meta();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -898,74 +930,133 @@ void Replayer<I>::handle_unlink_peer(int r) {
|
||||
notify_status_updated();
|
||||
}
|
||||
|
||||
refresh_local_image();
|
||||
load_local_image_meta();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::register_update_watcher() {
|
||||
void Replayer<I>::register_local_update_watcher() {
|
||||
dout(10) << dendl;
|
||||
|
||||
m_update_watch_ctx = new C_UpdateWatchCtx(this);
|
||||
int r = m_state_builder->remote_image_ctx->state->register_update_watcher(
|
||||
m_update_watch_ctx, &m_update_watcher_handle);
|
||||
|
||||
int r = m_state_builder->local_image_ctx->state->register_update_watcher(
|
||||
m_update_watch_ctx, &m_local_update_watcher_handle);
|
||||
auto ctx = create_context_callback<
|
||||
Replayer<I>, &Replayer<I>::handle_register_update_watcher>(this);
|
||||
Replayer<I>, &Replayer<I>::handle_register_local_update_watcher>(this);
|
||||
m_threads->work_queue->queue(ctx, r);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::handle_register_update_watcher(int r) {
|
||||
void Replayer<I>::handle_register_local_update_watcher(int r) {
|
||||
dout(10) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0) {
|
||||
derr << "failed to register update watcher: " << cpp_strerror(r) << dendl;
|
||||
handle_replay_complete(r, "failed to register remote image update watcher");
|
||||
derr << "failed to register local update watcher: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
handle_replay_complete(r, "failed to register local image update watcher");
|
||||
m_state = STATE_COMPLETE;
|
||||
|
||||
delete m_update_watch_ctx;
|
||||
m_update_watch_ctx = nullptr;
|
||||
} else {
|
||||
m_state = STATE_REPLAYING;
|
||||
|
||||
Context* on_init = nullptr;
|
||||
std::swap(on_init, m_on_init_shutdown);
|
||||
on_init->complete(r);
|
||||
return;
|
||||
}
|
||||
|
||||
register_remote_update_watcher();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::register_remote_update_watcher() {
|
||||
dout(10) << dendl;
|
||||
|
||||
int r = m_state_builder->remote_image_ctx->state->register_update_watcher(
|
||||
m_update_watch_ctx, &m_remote_update_watcher_handle);
|
||||
auto ctx = create_context_callback<
|
||||
Replayer<I>, &Replayer<I>::handle_register_remote_update_watcher>(this);
|
||||
m_threads->work_queue->queue(ctx, r);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::handle_register_remote_update_watcher(int r) {
|
||||
dout(10) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0) {
|
||||
derr << "failed to register remote update watcher: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
handle_replay_complete(r, "failed to register remote image update watcher");
|
||||
m_state = STATE_COMPLETE;
|
||||
|
||||
unregister_local_update_watcher();
|
||||
return;
|
||||
}
|
||||
|
||||
m_state = STATE_REPLAYING;
|
||||
|
||||
Context* on_init = nullptr;
|
||||
std::swap(on_init, m_on_init_shutdown);
|
||||
on_init->complete(r);
|
||||
on_init->complete(0);
|
||||
|
||||
// delay initial snapshot scan until after we have alerted
|
||||
// image replayer that we have initialized in case an error
|
||||
// occurs
|
||||
if (r >= 0) {
|
||||
{
|
||||
std::unique_lock locker{m_lock};
|
||||
notify_status_updated();
|
||||
}
|
||||
|
||||
refresh_local_image();
|
||||
{
|
||||
std::unique_lock locker{m_lock};
|
||||
notify_status_updated();
|
||||
}
|
||||
|
||||
load_local_image_meta();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::unregister_update_watcher() {
|
||||
void Replayer<I>::unregister_remote_update_watcher() {
|
||||
dout(10) << dendl;
|
||||
|
||||
auto ctx = create_context_callback<
|
||||
Replayer<I>,
|
||||
&Replayer<I>::handle_unregister_update_watcher>(this);
|
||||
&Replayer<I>::handle_unregister_remote_update_watcher>(this);
|
||||
m_state_builder->remote_image_ctx->state->unregister_update_watcher(
|
||||
m_update_watcher_handle, ctx);
|
||||
m_remote_update_watcher_handle, ctx);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::handle_unregister_update_watcher(int r) {
|
||||
void Replayer<I>::handle_unregister_remote_update_watcher(int r) {
|
||||
dout(10) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0) {
|
||||
derr << "failed to unregister update watcher: " << cpp_strerror(r) << dendl;
|
||||
derr << "failed to unregister remote update watcher: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
handle_replay_complete(
|
||||
r, "failed to unregister remote image update watcher");
|
||||
}
|
||||
|
||||
unregister_local_update_watcher();
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::unregister_local_update_watcher() {
|
||||
dout(10) << dendl;
|
||||
|
||||
auto ctx = create_context_callback<
|
||||
Replayer<I>,
|
||||
&Replayer<I>::handle_unregister_local_update_watcher>(this);
|
||||
m_state_builder->local_image_ctx->state->unregister_update_watcher(
|
||||
m_local_update_watcher_handle, ctx);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::handle_unregister_local_update_watcher(int r) {
|
||||
dout(10) << "r=" << r << dendl;
|
||||
|
||||
if (r < 0) {
|
||||
derr << "failed to unregister local update watcher: " << cpp_strerror(r)
|
||||
<< dendl;
|
||||
handle_replay_complete(
|
||||
r, "failed to unregister local image update watcher");
|
||||
}
|
||||
|
||||
delete m_update_watch_ctx;
|
||||
m_update_watch_ctx = nullptr;
|
||||
|
||||
@ -996,19 +1087,19 @@ void Replayer<I>::handle_wait_for_in_flight_ops(int r) {
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void Replayer<I>::handle_remote_image_update_notify() {
|
||||
void Replayer<I>::handle_image_update_notify() {
|
||||
dout(10) << dendl;
|
||||
|
||||
std::unique_lock locker{m_lock};
|
||||
if (m_state == STATE_REPLAYING) {
|
||||
dout(15) << "flagging snapshot rescan required" << dendl;
|
||||
m_remote_image_updated = true;
|
||||
m_image_updated = true;
|
||||
} else if (m_state == STATE_IDLE) {
|
||||
m_state = STATE_REPLAYING;
|
||||
locker.unlock();
|
||||
|
||||
dout(15) << "restarting idle replayer" << dendl;
|
||||
refresh_local_image();
|
||||
load_local_image_meta();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1062,7 +1153,7 @@ bool Replayer<I>::is_replay_interrupted(std::unique_lock<ceph::mutex>* locker) {
|
||||
locker->unlock();
|
||||
|
||||
dout(10) << "resuming pending shut down" << dendl;
|
||||
unregister_update_watcher();
|
||||
unregister_remote_update_watcher();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -73,9 +73,8 @@ public:
|
||||
}
|
||||
|
||||
bool is_resync_requested() const override {
|
||||
std::unique_lock locker(m_lock);
|
||||
// TODO
|
||||
return false;
|
||||
std::unique_lock locker{m_lock};
|
||||
return m_resync_requested;
|
||||
}
|
||||
|
||||
int get_error_code() const override {
|
||||
@ -95,10 +94,16 @@ private:
|
||||
* <init>
|
||||
* |
|
||||
* v
|
||||
* REGISTER_UPDATE_WATCHER
|
||||
* REGISTER_LOCAL_UPDATE_WATCHER
|
||||
* |
|
||||
* v (skip if not needed)
|
||||
* REFRESH_LOCAL_IMAGE <------------------------------\
|
||||
* v
|
||||
* REGISTER_REMOTE_UPDATE_WATCHER
|
||||
* |
|
||||
* v
|
||||
* LOAD_LOCAL_IMAGE_META <----------------------------\
|
||||
* | |
|
||||
* v (skip if not needed) |
|
||||
* REFRESH_LOCAL_IMAGE |
|
||||
* | |
|
||||
* v (skip if not needed) |
|
||||
* REFRESH_REMOTE_IMAGE |
|
||||
@ -149,7 +154,10 @@ private:
|
||||
* <shut down>
|
||||
* |
|
||||
* v
|
||||
* UNREGISTER_UPDATE_WATCHER
|
||||
* UNREGISTER_REMOTE_UPDATE_WATCHER
|
||||
* |
|
||||
* v
|
||||
* UNREGISTER_LOCAL_UPDATE_WATCHER
|
||||
* |
|
||||
* v
|
||||
* WAIT_FOR_IN_FLIGHT_OPS
|
||||
@ -183,11 +191,14 @@ private:
|
||||
|
||||
Context* m_on_init_shutdown = nullptr;
|
||||
|
||||
bool m_resync_requested = false;
|
||||
int m_error_code = 0;
|
||||
std::string m_error_description;
|
||||
|
||||
C_UpdateWatchCtx* m_update_watch_ctx;
|
||||
uint64_t m_update_watcher_handle = 0;
|
||||
uint64_t m_local_update_watcher_handle = 0;
|
||||
uint64_t m_remote_update_watcher_handle = 0;
|
||||
bool m_image_updated = false;
|
||||
|
||||
AsyncOpTracker m_in_flight_op_tracker;
|
||||
|
||||
@ -207,6 +218,9 @@ private:
|
||||
bool m_remote_image_updated = false;
|
||||
bool m_updating_sync_point = false;
|
||||
|
||||
void load_local_image_meta();
|
||||
void handle_load_local_image_meta(int r);
|
||||
|
||||
void refresh_local_image();
|
||||
void handle_refresh_local_image(int r);
|
||||
|
||||
@ -245,16 +259,22 @@ private:
|
||||
void unlink_peer();
|
||||
void handle_unlink_peer(int r);
|
||||
|
||||
void register_update_watcher();
|
||||
void handle_register_update_watcher(int r);
|
||||
void register_local_update_watcher();
|
||||
void handle_register_local_update_watcher(int r);
|
||||
|
||||
void unregister_update_watcher();
|
||||
void handle_unregister_update_watcher(int r);
|
||||
void register_remote_update_watcher();
|
||||
void handle_register_remote_update_watcher(int r);
|
||||
|
||||
void unregister_remote_update_watcher();
|
||||
void handle_unregister_remote_update_watcher(int r);
|
||||
|
||||
void unregister_local_update_watcher();
|
||||
void handle_unregister_local_update_watcher(int r);
|
||||
|
||||
void wait_for_in_flight_ops();
|
||||
void handle_wait_for_in_flight_ops(int r);
|
||||
|
||||
void handle_remote_image_update_notify();
|
||||
void handle_image_update_notify();
|
||||
|
||||
void handle_replay_complete(int r, const std::string& description);
|
||||
void handle_replay_complete(std::unique_lock<ceph::mutex>* locker,
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/errno.h"
|
||||
#include "librbd/ImageCtx.h"
|
||||
#include "librbd/mirror/snapshot/ImageMeta.h"
|
||||
#include "tools/rbd_mirror/image_replayer/snapshot/CreateLocalImageRequest.h"
|
||||
#include "tools/rbd_mirror/image_replayer/snapshot/PrepareReplayRequest.h"
|
||||
#include "tools/rbd_mirror/image_replayer/snapshot/Replayer.h"
|
||||
@ -30,12 +31,16 @@ StateBuilder<I>::StateBuilder(const std::string& global_image_id)
|
||||
|
||||
template <typename I>
|
||||
StateBuilder<I>::~StateBuilder() {
|
||||
ceph_assert(local_image_meta == nullptr);
|
||||
}
|
||||
|
||||
template <typename I>
|
||||
void StateBuilder<I>::close(Context* on_finish) {
|
||||
dout(10) << dendl;
|
||||
|
||||
delete local_image_meta;
|
||||
local_image_meta = nullptr;
|
||||
|
||||
// close the remote image after closing the local
|
||||
// image in case the remote cluster is unreachable and
|
||||
// we cannot close it.
|
||||
|
@ -9,7 +9,18 @@
|
||||
|
||||
struct Context;
|
||||
|
||||
namespace librbd { struct ImageCtx; }
|
||||
namespace librbd {
|
||||
|
||||
struct ImageCtx;
|
||||
|
||||
namespace mirror {
|
||||
namespace snapshot {
|
||||
|
||||
template <typename> class ImageMeta;
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace mirror
|
||||
} // namespace librbd
|
||||
|
||||
namespace rbd {
|
||||
namespace mirror {
|
||||
@ -66,6 +77,7 @@ public:
|
||||
|
||||
std::string remote_mirror_peer_uuid;
|
||||
|
||||
librbd::mirror::snapshot::ImageMeta<ImageCtxT>* local_image_meta = nullptr;
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
|
Loading…
Reference in New Issue
Block a user