return GROUP_GLOBAL_KEY_PREFIX + global_id;
}
+std::string group_resync_key(const std::string& global_group_id,
+ const std::string& group_name) {
+ return group_name + "_" + global_group_id;
+}
+
std::string group_remote_status_global_key(const std::string& global_id,
const std::string& mirror_uuid) {
return GROUP_REMOTE_STATUS_GLOBAL_KEY_PREFIX + global_id + "_" + mirror_uuid;
return 0;
}
+/**
+ * Input:
+ * @param global_group_id (std::string)
+ * @param global_name (std::string)
+ *
+ * Output:
+ * @param group_id (std::string)
+ * @returns 0 on success, negative error code on failure
+ */
+int mirror_group_resync_get(cls_method_context_t hctx, bufferlist *in,
+ bufferlist *out) {
+ std::string global_group_id;
+ std::string group_name;
+ try {
+ auto it = in->cbegin();
+ decode(global_group_id, it);
+ decode(group_name, it);
+ } catch (const ceph::buffer::error &err) {
+ return -EINVAL;
+ }
+
+ std::string search = mirror::group_resync_key(global_group_id, group_name);
+ std::string last_read = group_name;
+ int max_read = 5;
+ bool more = true;
+ do {
+ std::set<std::string> keys;
+ int r = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys, &more);
+ if (r < 0) {
+ CLS_ERR("error reading group resync keys, global_id '%s': '%s'",
+ global_group_id.c_str(), cpp_strerror(r).c_str());
+ return r;
+ }
+
+ for (auto& key : keys) {
+ if (key == search) {
+ r = cls_cxx_map_get_val(hctx, key, out);
+ if (r < 0) {
+ CLS_ERR("error reading group_id for group resync, global_id '%s': '%s'",
+ global_group_id.c_str(), cpp_strerror(r).c_str());
+ return r;
+ }
+ return 0;
+ }
+ }
+
+ if (!keys.empty()) {
+ last_read = *keys.rbegin();
+ }
+ } while (more);
+
+ // shouldn't reach here
+ return -EINVAL;
+}
+
+/**
+ * Input:
+ * @param global_group_id (std::string)
+ * @param global_name (std::string)
+ * @param group_id (std::string)
+ *
+ * Output:
+ * @returns 0 on success, negative error code on failure
+ */
+int mirror_group_resync_set(cls_method_context_t hctx, bufferlist *in,
+ bufferlist *out) {
+ std::string global_group_id;
+ std::string group_name;
+ std::string group_id;
+ try {
+ auto it = in->cbegin();
+ decode(global_group_id, it);
+ decode(group_name, it);
+ decode(group_id, it);
+ } catch (const ceph::buffer::error &err) {
+ return -EINVAL;
+ }
+ std::string key = mirror::group_resync_key(global_group_id, group_name);
+ bufferlist val_bl;
+ encode(group_id, val_bl);
+ int r = cls_cxx_map_set_val(hctx, key, &val_bl);
+ if (r < 0) {
+ CLS_ERR("error setting key %s on mirror group resync object: %s",
+ key.c_str(), cpp_strerror(r).c_str());
+ return r;
+ }
+
+ return 0;
+}
+
+/**
+ * Input:
+ * @param global_group_id (std::string)
+ * @param global_name (std::string)
+ *
+ * Output:
+ * @returns 0 on success, negative error code on failure
+ */
+int mirror_group_resync_remove(cls_method_context_t hctx, bufferlist *in,
+ bufferlist *out) {
+ std::string global_group_id;
+ std::string group_name;
+ try {
+ auto it = in->cbegin();
+ decode(global_group_id, it);
+ decode(group_name, it);
+ } catch (const ceph::buffer::error &err) {
+ return -EINVAL;
+ }
+
+ std::string key = mirror::group_resync_key(global_group_id, group_name);
+ int r = cls_cxx_map_remove_key(hctx, key);
+ if (r < 0) {
+ CLS_ERR("error removing key %s from mirror group resync object: %s",
+ key.c_str(), cpp_strerror(r).c_str());
+ return r;
+ }
+
+ return 0;
+}
+
/**
* Input:
* @param global_id (std::string)
cls_method_handle_t h_mirror_image_snapshot_unlink_peer;
cls_method_handle_t h_mirror_image_snapshot_set_copy_progress;
cls_method_handle_t h_mirror_group_list;
+ cls_method_handle_t h_mirror_group_resync_get;
+ cls_method_handle_t h_mirror_group_resync_set;
+ cls_method_handle_t h_mirror_group_resync_remove;
cls_method_handle_t h_mirror_group_get_group_id;
cls_method_handle_t h_mirror_group_get;
cls_method_handle_t h_mirror_group_set;
&h_mirror_image_snapshot_set_copy_progress);
cls_register_cxx_method(h_class, "mirror_group_list", CLS_METHOD_RD,
mirror_group_list, &h_mirror_group_list);
+ cls_register_cxx_method(h_class, "mirror_group_resync_get", CLS_METHOD_RD,
+ mirror_group_resync_get,
+ &h_mirror_group_resync_get);
+ cls_register_cxx_method(h_class, "mirror_group_resync_set",
+ CLS_METHOD_RD | CLS_METHOD_WR,
+ mirror_group_resync_set, &h_mirror_group_resync_set);
+ cls_register_cxx_method(h_class, "mirror_group_resync_remove",
+ CLS_METHOD_RD | CLS_METHOD_WR,
+ mirror_group_resync_remove,
+ &h_mirror_group_resync_remove);
cls_register_cxx_method(h_class, "mirror_group_get_group_id", CLS_METHOD_RD,
mirror_group_get_group_id,
&h_mirror_group_get_group_id);
return mirror_group_list_finish(&bl_it, groups);
}
+void mirror_group_resync_get_start(librados::ObjectReadOperation *op,
+ const std::string &global_group_id,
+ const std::string &group_name)
+{
+ bufferlist in_bl;
+ encode(global_group_id, in_bl);
+ encode(group_name, in_bl);
+ op->exec("rbd", "mirror_group_resync_get", in_bl);
+}
+
+int mirror_group_resync_get_finish(bufferlist::const_iterator *it,
+ std::string *group_id) {
+ try {
+ decode(*group_id, *it);
+ } catch (const ceph::buffer::error &err) {
+ return -EBADMSG;
+ }
+ return 0;
+}
+
+int mirror_group_resync_get(librados::IoCtx *ioctx,
+ const std::string &global_group_id,
+ const std::string &group_name,
+ std::string *group_id)
+{
+ librados::ObjectReadOperation op;
+ mirror_group_resync_get_start(&op, global_group_id, group_name);
+
+ bufferlist out_bl;
+ int r = ioctx->operate(RBD_GROUP_RESYNC, &op, &out_bl);
+ if (r < 0) {
+ return r;
+ }
+
+ auto bl_it = out_bl.cbegin();
+ return mirror_group_resync_get_finish(&bl_it, group_id);
+}
+
+void mirror_group_resync_set(librados::ObjectWriteOperation *op,
+ const std::string &global_group_id,
+ const std::string &group_name,
+ const std::string &group_id) {
+ bufferlist bl;
+ encode(global_group_id, bl);
+ encode(group_name, bl);
+ encode(group_id, bl);
+
+ op->exec("rbd", "mirror_group_resync_set", bl);
+}
+
+int mirror_group_resync_set(librados::IoCtx *ioctx,
+ const std::string &global_group_id,
+ const std::string &group_name,
+ const std::string &group_id) {
+ librados::ObjectWriteOperation op;
+ mirror_group_resync_set(&op, global_group_id, group_name, group_id);
+
+ int r = ioctx->operate(RBD_GROUP_RESYNC, &op);
+ if (r < 0) {
+ return r;
+ }
+ return 0;
+}
+
+void mirror_group_resync_remove(librados::ObjectWriteOperation *op,
+ const std::string &global_group_id,
+ const std::string &group_name) {
+ bufferlist bl;
+ encode(global_group_id, bl);
+ encode(group_name, bl);
+
+ op->exec("rbd", "mirror_group_resync_remove", bl);
+}
+
+int mirror_group_resync_remove(librados::IoCtx *ioctx,
+ const std::string &global_group_id,
+ const std::string &group_name) {
+ librados::ObjectWriteOperation op;
+ mirror_group_resync_remove(&op, global_group_id, group_name);
+
+ int r = ioctx->operate(RBD_GROUP_RESYNC, &op);
+ if (r < 0) {
+ return r;
+ }
+ return 0;
+}
+
void mirror_group_get_group_id_start(librados::ObjectReadOperation *op,
const std::string &global_group_id) {
bufferlist in_bl;
int mirror_group_list(librados::IoCtx *ioctx,
const std::string &start, uint64_t max_return,
std::map<std::string, cls::rbd::MirrorGroup> *groups);
+void mirror_group_resync_get_start(librados::ObjectReadOperation *op,
+ const std::string &global_group_id,
+ const std::string &group_name);
+int mirror_group_resync_get_finish(bufferlist::const_iterator *it,
+ std::string *group_id);
+int mirror_group_resync_get(librados::IoCtx *ioctx,
+ const std::string &global_group_id,
+ const std::string &group_name,
+ std::string *group_id);
+void mirror_group_resync_set(librados::ObjectWriteOperation *op,
+ const std::string &global_group_id,
+ const std::string &group_name,
+ const std::string &group_id);
+int mirror_group_resync_set(librados::IoCtx *ioctx,
+ const std::string &global_group_id,
+ const std::string &group_name,
+ const std::string &group_id);
+void mirror_group_resync_remove(librados::ObjectWriteOperation *op,
+ const std::string &global_group_id,
+ const std::string &group_name);
+int mirror_group_resync_remove(librados::IoCtx *ioctx,
+ const std::string &global_group_id,
+ const std::string &group_name);
void mirror_group_get_group_id_start(librados::ObjectReadOperation *op,
const std::string &global_group_id);
int mirror_group_get_group_id_finish(ceph::buffer::list::const_iterator *it,
#define RBD_GROUP_DIRECTORY "rbd_group_directory"
+#define RBD_GROUP_RESYNC "rbd_group_resync"
+
#define RBD_TRASH "rbd_trash"
/**
CephContext *cct = (CephContext *)group_ioctx.cct();
ldout(cct, 20) << "io_ctx=" << &group_ioctx
<< ", group_name=" << group_name << dendl;
-
std::string group_id;
int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
group_name, &group_id);
}
if (state == cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY) {
- lderr(cct) << "group " << group_name
+ lderr(cct) << "group=" << group_name
<< " is primary, cannot resync to itself" << dendl;
return -EINVAL;
}
- // TODO: implement the group resync functionality
+ r = cls_client::mirror_group_resync_set(&group_ioctx,
+ mirror_group.global_group_id,
+ group_name, group_id);
+ if (r < 0) {
+ lderr(cct) << "setting group resync with global_group_id="
+ << mirror_group.global_group_id << " failed: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ std::vector<cls::rbd::GroupImageStatus> images;
+ r = Group<I>::group_image_list_by_id(group_ioctx, group_id, &images);
+ if (r < 0) {
+ lderr(cct) << "listing images in group=" << group_name
+ << " failed: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ r = MirroringWatcher<I>::notify_group_updated(
+ group_ioctx, cls::rbd::MIRROR_GROUP_STATE_DISABLED, group_id,
+ mirror_group.global_group_id, images.size());
+ if (r < 0) {
+ lderr(cct) << "failed to notify mirroring group=" << group_name
+ << " updated: " << cpp_strerror(r) << dendl;
+ // not fatal
+ }
return 0;
}
}
MOCK_METHOD0(destroy, void());
- MOCK_METHOD2(start, void(Context *, bool));
+ MOCK_METHOD4(start, void(Context *, bool, bool, bool));
MOCK_METHOD2(stop, void(Context *, bool));
- MOCK_METHOD1(restart, void(Context*));
+ MOCK_METHOD2(restart, void(Context*, bool));
MOCK_METHOD0(flush, void());
+ MOCK_METHOD0(sync_group_names, void());
MOCK_METHOD1(print_status, void(Formatter *));
MOCK_METHOD1(add_peer, void(const Peer<librbd::MockTestImageCtx>& peer));
+ MOCK_METHOD0(get_name, const std::string &());
MOCK_METHOD0(get_global_group_id, const std::string &());
+ MOCK_METHOD0(get_local_group_id, const std::string &());
MOCK_METHOD0(is_running, bool());
MOCK_METHOD0(is_stopped, bool());
MOCK_METHOD0(is_blocklisted, bool());
}
template <typename I>
-void GroupReplayer<I>::start(Context *on_finish, bool manual, bool restart) {
+void GroupReplayer<I>::start(Context *on_finish, bool manual,
+ bool restart, bool resync) {
dout(10) << "on_finish=" << on_finish << ", manual=" << manual
- << ", restart=" << restart << dendl;
+ << ", restart=" << restart << ", resync=" << resync << dendl;
int r = 0;
{
m_image_replayer_index.clear();
m_get_remote_group_snap_ret_vals.clear();
m_manual_stop = false;
+ m_finished = false;
+ if (resync) {
+ m_resync_requested = true;
+ }
ceph_assert(m_on_start_finish == nullptr);
std::swap(m_on_start_finish, on_finish);
}
}
template <typename I>
-void GroupReplayer<I>::restart(Context *on_finish) {
- dout(10) << dendl;
+void GroupReplayer<I>::restart(Context *on_finish, bool resync) {
+ dout(10) << "resync=" << resync << dendl;
{
std::lock_guard locker{m_lock};
+ if (m_resync_requested) {
+ dout(10) << "resync is already in progress, cancelling restart" << dendl;
+ on_finish->complete(-ECANCELED);
+ return;
+ }
m_restart_requested = true;
m_on_start_finish = nullptr;
+ if (resync) {
+ m_resync_requested = true;
+ }
}
auto ctx = new LambdaContext(
- [this, on_finish](int r) {
+ [this, on_finish, resync](int r) {
if (r < 0) {
// Try start anyway.
}
- start(on_finish, true, true);
+ start(on_finish, true, true, resync);
});
stop(ctx, false, true);
}
m_threads, m_local_io_ctx, m_remote_group_peer.io_ctx, m_global_group_id,
m_local_mirror_uuid, m_instance_watcher, m_local_status_updater,
m_remote_group_peer.mirror_status_updater, m_cache_manager_handler,
- m_pool_meta_cache, &m_local_group_id, &m_remote_group_id,
- &m_local_group_snaps, &m_local_group_ctx, &m_image_replayers,
- &m_image_replayer_index, ctx);
+ m_pool_meta_cache, m_resync_requested, &m_local_group_id,
+ &m_remote_group_id, &m_local_group_snaps, &m_local_group_ctx,
+ &m_image_replayers, &m_image_replayer_index, ctx);
request->get();
m_bootstrap_request = request;
dout(10) << "r=" << r << dendl;
{
std::lock_guard locker{m_lock};
+ m_resync_requested = false;
if (m_state == STATE_STOPPING || m_state == STATE_STOPPED) {
dout(10) << "stop prevailed" <<dendl;
return;
auto requests_it = m_create_snap_requests.find(remote_group_snap_id);
if (requests_it == m_create_snap_requests.end()) {
- ceph_assert(m_local_group_snaps.count(remote_group_snap_id) == 0);
-
requests_it = m_create_snap_requests.insert(
{remote_group_snap_id, {}}).first;
}
m_get_remote_group_snap_ret_vals[remote_group_snap_id] = r;
-
maybe_create_mirror_snapshot(locker, remote_group_snap_id);
}
std::string get_name() const {
std::lock_guard l{m_lock};
- return m_group_spec;
+ return m_local_group_name;
}
void set_state_description(int r, const std::string &desc);
inline const std::string& get_global_group_id() const {
return m_global_group_id;
}
+ inline const std::string& get_local_group_id() const {
+ return m_local_group_id;
+ }
void start(Context *on_finish = nullptr, bool manual = false,
- bool restart = false);
+ bool restart = false, bool resync = false);
void stop(Context *on_finish = nullptr, bool manual = false,
bool restart = false);
- void restart(Context *on_finish = nullptr);
+ void restart(Context *on_finish = nullptr, bool resync = false);
void flush();
void print_status(Formatter *f);
Context *m_on_start_finish = nullptr;
Context *m_on_stop_finish = nullptr;
bool m_stop_requested = false;
+ bool m_resync_requested = false;
bool m_restart_requested = false;
bool m_manual_stop = false;
bool m_finished = false;
// vim: ts=8 sw=2 smarttab
#include "include/stringify.h"
+#include "cls/rbd/cls_rbd_client.h"
#include "common/Cond.h"
#include "common/Timer.h"
#include "common/debug.h"
ceph_assert(ceph_mutex_is_locked(m_lock));
std::string global_group_id = group_replayer->get_global_group_id();
+ std::string group_name = group_replayer->get_name();
+ std::string group_id;
+ bool resync_requested = false;
+ int r = librbd::cls_client::mirror_group_resync_get(&m_local_io_ctx,
+ global_group_id,
+ group_name,
+ &group_id);
+ if (r < 0) {
+ derr << "getting mirror group resync for global_group_id="
+ << global_group_id << " failed: " << cpp_strerror(r) << dendl;
+ } else if (r == 0) {
+ if (group_id == group_replayer->get_local_group_id()) {
+ resync_requested = true;
+ }
+ }
if (!group_replayer->is_stopped()) {
- if (group_replayer->needs_restart()) {
- stop_group_replayer(group_replayer, new C_TrackedOp(m_async_op_tracker,
- nullptr));
+ if (group_replayer->needs_restart() || resync_requested) {
+ group_replayer->restart(new C_TrackedOp(m_async_op_tracker, nullptr),
+ resync_requested);
} else {
group_replayer->sync_group_names();
}
return;
} else if (group_replayer->is_finished()) {
// TODO temporary until policy integrated
- dout(5) << "removing group replayer for global_group_id="
- << global_group_id << dendl;
- m_group_replayers.erase(group_replayer->get_global_group_id());
- group_replayer->destroy();
- return;
+ if (resync_requested) {
+ resync_requested = false;
+ r = librbd::cls_client::mirror_group_resync_remove(&m_local_io_ctx,
+ global_group_id,
+ group_name);
+ if (r < 0) {
+ derr << "removing mirror group resync for global_group_id="
+ << global_group_id << " failed: " << cpp_strerror(r) << dendl;
+ }
+ } else {
+ dout(5) << "removing group replayer for global_group_id="
+ << global_group_id << dendl;
+ m_group_replayers.erase(group_replayer->get_global_group_id());
+ group_replayer->destroy();
+ return;
+ }
} else if (m_manual_stop) {
return;
}
-
dout(10) << "global_group_id=" << global_group_id << dendl;
- group_replayer->start(new C_TrackedOp(m_async_op_tracker, nullptr), false);
+ group_replayer->start(new C_TrackedOp(m_async_op_tracker, nullptr),
+ false, false, resync_requested);
}
template <typename I>
MirrorStatusUpdater<I> *remote_status_updater,
journal::CacheManagerHandler *cache_manager_handler,
PoolMetaCache *pool_meta_cache,
+ bool resync_requested,
std::string *local_group_id,
std::string *remote_group_id,
std::map<std::string, cls::rbd::GroupSnapshot> *local_group_snaps,
m_remote_status_updater(remote_status_updater),
m_cache_manager_handler(cache_manager_handler),
m_pool_meta_cache(pool_meta_cache),
+ m_resync_requested(resync_requested),
m_local_group_id(local_group_id),
m_remote_group_id(remote_group_id),
m_local_group_snaps(local_group_snaps),
template <typename I>
void BootstrapRequest<I>::send() {
- get_remote_group_id();
+ if (m_resync_requested) {
+ get_local_group_id();
+ } else {
+ get_remote_group_id();
+ }
}
template <typename I>
if (r == -ENOENT) {
derr << "failed to find local mirror group snapshot" << dendl;
} else {
- if (state == cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED) {
+ if (m_remote_mirror_group_primary &&
+ state == cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED) {
// if local snapshot is primary demoted, check if there is demote snapshot
// in remote, if not then split brain
- if (!is_demoted_snap_exists(remote_group_snaps)) {
+ if (!is_demoted_snap_exists(remote_group_snaps) && !m_resync_requested) {
finish(-EEXIST);
return;
}
&BootstrapRequest<I>::handle_move_local_image_to_trash>(this);
auto req = image_deleter::TrashMoveRequest<I>::create(
- m_image_io_ctx, global_image_id, false, m_threads->work_queue, ctx);
+ m_image_io_ctx, global_image_id, m_resync_requested,
+ m_threads->work_queue, ctx);
req->send();
}
MirrorStatusUpdater<ImageCtxT> *remote_status_updater,
journal::CacheManagerHandler *cache_manager_handler,
PoolMetaCache *pool_meta_cache,
+ bool resync_requested,
std::string *local_group_id,
std::string *remote_group_id,
std::map<std::string, cls::rbd::GroupSnapshot> *local_group_snaps,
return new BootstrapRequest(
threads, local_io_ctx, remote_io_ctx, global_group_id, local_mirror_uuid,
instance_watcher, local_status_updater, remote_status_updater,
- cache_manager_handler, pool_meta_cache, local_group_id, remote_group_id,
- local_group_snaps, local_group_ctx, image_replayers, image_replayer_index,
- on_finish);
+ cache_manager_handler, pool_meta_cache, resync_requested, local_group_id,
+ remote_group_id, local_group_snaps, local_group_ctx, image_replayers,
+ image_replayer_index, on_finish);
}
BootstrapRequest(
MirrorStatusUpdater<ImageCtxT> *remote_status_updater,
journal::CacheManagerHandler *cache_manager_handler,
PoolMetaCache *pool_meta_cache,
+ bool resync_requested,
std::string *local_group_id,
std::string *remote_group_id,
std::map<std::string, cls::rbd::GroupSnapshot> *local_group_snaps,
MirrorStatusUpdater<ImageCtxT> *m_remote_status_updater;
journal::CacheManagerHandler *m_cache_manager_handler;
PoolMetaCache *m_pool_meta_cache;
+ bool m_resync_requested = false;
std::string *m_local_group_id;
std::string *m_remote_group_id;
std::map<std::string, cls::rbd::GroupSnapshot> *m_local_group_snaps;
m_prune_snap_id = *prune_snap_ids.begin();
dout(5) << "pruning unused non-primary snapshot " << m_prune_snap_id << dendl;
- unlink_group_snapshot();
+ prune_non_primary_snapshot();
+ //unlink_group_snapshot(); //PK: FIXME
return;
}