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:
Mykola Golub 2020-02-27 18:26:41 +02:00 committed by GitHub
commit f1c7155713
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1211 additions and 268 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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();
}

View File

@ -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)});

View File

@ -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;

View File

@ -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();
}

View File

@ -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();

View 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>;

View 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

View File

@ -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};

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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;

View 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

View File

@ -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,

View File

@ -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));
}

View File

@ -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());

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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.

View File

@ -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