inline bool operator<(const SnapshotNamespaceVariant& sn) const {
return static_cast<const SnapshotNamespaceVariant&>(*this) < sn;
}
+ inline bool operator!=(const SnapshotNamespaceVariant& sn) const {
+ return !(*this == sn);
+ }
};
WRITE_CLASS_ENCODER(SnapshotNamespace);
mirror/snapshot/CreateNonPrimaryRequest.cc
mirror/snapshot/CreatePrimaryRequest.cc
mirror/snapshot/DemoteRequest.cc
+ mirror/snapshot/GetImageStateRequest.cc
mirror/snapshot/PromoteRequest.cc
+ mirror/snapshot/RemoveImageStateRequest.cc
+ mirror/snapshot/SetImageStateRequest.cc
+ mirror/snapshot/Types.cc
mirror/snapshot/UnlinkPeerRequest.cc
+ mirror/snapshot/WriteImageStateRequest.cc
object_map/CreateRequest.cc
object_map/InvalidateRequest.cc
object_map/LockRequest.cc
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/mirror/snapshot/GetImageStateRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/Utils.h"
+#include "librbd/mirror/snapshot/Types.h"
+#include "librbd/mirror/snapshot/Utils.h"
+
+#define dout_subsys ceph_subsys_rbd
+
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::mirror::snapshot::GetImageStateRequest: " \
+ << this << " " << __func__ << ": "
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using librbd::util::create_rados_callback;
+
+template <typename I>
+void GetImageStateRequest<I>::send() {
+ read_object();
+}
+
+
+template <typename I>
+void GetImageStateRequest<I>::read_object() {
+ CephContext *cct = m_image_ctx->cct;
+
+ auto oid = util::image_state_object_name(m_image_ctx, m_snap_id,
+ m_object_index);
+ ldout(cct, 20) << oid << dendl;
+
+ librados::ObjectReadOperation op;
+ m_bl.clear();
+ op.read(0, 0, &m_bl, nullptr);
+
+ librados::AioCompletion *comp = create_rados_callback<
+ GetImageStateRequest<I>,
+ &GetImageStateRequest<I>::handle_read_object>(this);
+ int r = m_image_ctx->md_ctx.aio_operate(oid, comp, &op, nullptr);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+void GetImageStateRequest<I>::handle_read_object(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to read image state object: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ auto iter = m_bl.cbegin();
+
+ if (m_object_index == 0) {
+ ImageStateHeader header;
+ try {
+ using ceph::decode;
+ decode(header, iter);
+ } catch (const buffer::error &err) {
+ lderr(cct) << "failed to decode image state object header" << dendl;
+ finish(-EBADMSG);
+ return;
+ }
+ m_object_count = header.object_count;
+ }
+
+ bufferlist bl;
+ bl.substr_of(m_bl, iter.get_off(), m_bl.length() - iter.get_off());
+ m_state_bl.claim_append(bl);
+
+ m_object_index++;
+
+ if (m_object_index >= m_object_count) {
+ finish(0);
+ return;
+ }
+
+ read_object();
+}
+
+template <typename I>
+void GetImageStateRequest<I>::finish(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r == 0) {
+ try {
+ using ceph::decode;
+ decode(*m_image_state, m_state_bl);
+ } catch (const buffer::error &err) {
+ lderr(cct) << "failed to decode image state" << dendl;
+ r = -EBADMSG;
+ }
+ }
+
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+template class librbd::mirror::snapshot::GetImageStateRequest<librbd::ImageCtx>;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_GET_IMAGE_STATE_REQUEST_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_GET_IMAGE_STATE_REQUEST_H
+
+#include "include/buffer.h"
+#include "include/types.h"
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace mirror {
+namespace snapshot {
+
+struct ImageState;
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class GetImageStateRequest {
+public:
+ static GetImageStateRequest *create(ImageCtxT *image_ctx, uint64_t snap_id,
+ ImageState *image_state,
+ Context *on_finish) {
+ return new GetImageStateRequest(image_ctx, snap_id, image_state, on_finish);
+ }
+
+ GetImageStateRequest(ImageCtxT *image_ctx, uint64_t snap_id,
+ ImageState *image_state, Context *on_finish)
+ : m_image_ctx(image_ctx), m_snap_id(snap_id), m_image_state(image_state),
+ m_on_finish(on_finish) {
+ }
+
+ void send();
+
+private:
+ /**
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * READ_OBJECT (repeat for
+ * | every object)
+ * v
+ * <finish>
+ *
+ * @endverbatim
+ */
+
+ ImageCtxT *m_image_ctx;
+ uint64_t m_snap_id;
+ ImageState *m_image_state;
+ Context *m_on_finish;
+
+ bufferlist m_bl;
+ bufferlist m_state_bl;
+
+ size_t m_object_count = 0;
+ size_t m_object_index = 0;
+
+ void read_object();
+ void handle_read_object(int r);
+
+ void finish(int r);
+};
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+extern template class librbd::mirror::snapshot::GetImageStateRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_GET_IMAGE_STATE_REQUEST_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/Utils.h"
+#include "librbd/mirror/snapshot/Types.h"
+#include "librbd/mirror/snapshot/Utils.h"
+
+#define dout_subsys ceph_subsys_rbd
+
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::mirror::snapshot::RemoveImageStateRequest: " \
+ << this << " " << __func__ << ": "
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using librbd::util::create_rados_callback;
+
+template <typename I>
+void RemoveImageStateRequest<I>::send() {
+ get_object_count();
+}
+
+
+template <typename I>
+void RemoveImageStateRequest<I>::get_object_count() {
+ CephContext *cct = m_image_ctx->cct;
+
+ auto oid = util::image_state_object_name(m_image_ctx, m_snap_id, 0);
+ ldout(cct, 20) << oid << dendl;
+
+ librados::ObjectReadOperation op;
+ op.read(0, 0, &m_bl, nullptr);
+
+ librados::AioCompletion *comp = create_rados_callback<
+ RemoveImageStateRequest<I>,
+ &RemoveImageStateRequest<I>::handle_get_object_count>(this);
+ int r = m_image_ctx->md_ctx.aio_operate(oid, comp, &op, nullptr);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+void RemoveImageStateRequest<I>::handle_get_object_count(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to read image state object: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ ImageStateHeader header(1);
+ auto iter = m_bl.cbegin();
+ try {
+ using ceph::decode;
+
+ decode(header, iter);
+ } catch (const buffer::error &err) {
+ lderr(cct) << "failed to decode image state object header" << dendl;
+ // still try to remove it
+ }
+
+ m_object_count = header.object_count > 0 ? header.object_count : 1;
+
+ remove_object();
+}
+
+template <typename I>
+void RemoveImageStateRequest<I>::remove_object() {
+ CephContext *cct = m_image_ctx->cct;
+
+ ceph_assert(m_object_count > 0);
+ m_object_count--;
+
+ auto oid = util::image_state_object_name(m_image_ctx, m_snap_id,
+ m_object_count);
+ ldout(cct, 20) << oid << dendl;
+
+ librados::ObjectWriteOperation op;
+ op.remove();
+
+ librados::AioCompletion *comp = create_rados_callback<
+ RemoveImageStateRequest<I>,
+ &RemoveImageStateRequest<I>::handle_remove_object>(this);
+ int r = m_image_ctx->md_ctx.aio_operate(oid, comp, &op);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+void RemoveImageStateRequest<I>::handle_remove_object(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r < 0 && r != -ENOENT) {
+ lderr(cct) << "failed to remove image state object: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ if (m_object_count == 0) {
+ finish(0);
+ return;
+ }
+
+ remove_object();
+}
+
+template <typename I>
+void RemoveImageStateRequest<I>::finish(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+template class librbd::mirror::snapshot::RemoveImageStateRequest<librbd::ImageCtx>;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_REMOVE_IMAGE_STATE_REQUEST_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_REMOVE_IMAGE_STATE_REQUEST_H
+
+#include "include/buffer.h"
+#include "include/types.h"
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace mirror {
+namespace snapshot {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class RemoveImageStateRequest {
+public:
+ static RemoveImageStateRequest *create(ImageCtxT *image_ctx, uint64_t snap_id,
+ Context *on_finish) {
+ return new RemoveImageStateRequest(image_ctx, snap_id, on_finish);
+ }
+
+ RemoveImageStateRequest(ImageCtxT *image_ctx, uint64_t snap_id,
+ Context *on_finish)
+ : m_image_ctx(image_ctx), m_snap_id(snap_id), m_on_finish(on_finish) {
+ }
+
+ void send();
+
+private:
+ /**
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * GET_OBJECT_COUNT
+ * |
+ * v
+ * REMOVE_OBJECT (repeat for
+ * | every object)
+ * v
+ * <finish>
+ *
+ * @endverbatim
+ */
+
+ ImageCtxT *m_image_ctx;
+ uint64_t m_snap_id;
+ Context *m_on_finish;
+
+ bufferlist m_bl;
+
+ size_t m_object_count = 0;
+
+ void get_object_count();
+ void handle_get_object_count(int r);
+
+ void remove_object();
+ void handle_remove_object(int r);
+
+ void finish(int r);
+};
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+extern template class librbd::mirror::snapshot::RemoveImageStateRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_REMOVE_IMAGE_STATE_REQUEST_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/Utils.h"
+#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+#define dout_subsys ceph_subsys_rbd
+
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::mirror_snapshot::SetImageStateRequest: " \
+ << this << " " << __func__ << ": "
+
+namespace {
+
+const uint64_t MAX_METADATA_ITEMS = 128;
+
+}
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+using librbd::util::create_context_callback;
+using librbd::util::create_rados_callback;
+
+template <typename I>
+void SetImageStateRequest<I>::send() {
+ get_snap_limit();
+}
+
+template <typename I>
+void SetImageStateRequest<I>::get_snap_limit() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::snapshot_get_limit_start(&op);
+
+ librados::AioCompletion *comp = create_rados_callback<
+ SetImageStateRequest<I>,
+ &SetImageStateRequest<I>::handle_get_snap_limit>(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();
+}
+
+template <typename I>
+void SetImageStateRequest<I>::handle_get_snap_limit(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r == 0) {
+ auto it = m_bl.cbegin();
+ r = cls_client::snapshot_get_limit_finish(&it, &m_image_state.snap_limit);
+ }
+
+ if (r < 0) {
+ lderr(cct) << "failed to retrieve snapshot limit: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ get_metadata();
+}
+
+template <typename I>
+void SetImageStateRequest<I>::get_metadata() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "start_key=" << m_last_metadata_key << 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();
+}
+
+template <typename I>
+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;
+ finish(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};
+
+ m_image_state.name = m_image_ctx->name;
+ m_image_state.features = m_image_ctx->features;
+
+ for (auto &[snap_id, snap_info] : m_image_ctx->snap_info) {
+ auto type = cls::rbd::get_snap_namespace_type(snap_info.snap_namespace);
+ if (type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY ||
+ type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY) {
+ continue;
+ }
+ m_image_state.snapshots[snap_id] = {snap_id, snap_info.snap_namespace,
+ snap_info.name,
+ snap_info.protection_status};
+ }
+ }
+
+ write_image_state();
+}
+
+template <typename I>
+void SetImageStateRequest<I>::write_image_state() {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << dendl;
+
+ auto ctx = create_context_callback<
+ SetImageStateRequest<I>,
+ &SetImageStateRequest<I>::handle_write_image_state>(this);
+
+ auto req = WriteImageStateRequest<I>::create(m_image_ctx, m_snap_id,
+ m_image_state, ctx);
+ req->send();
+}
+
+template <typename I>
+void SetImageStateRequest<I>::handle_write_image_state(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to write image state: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ finish(0);
+}
+
+template <typename I>
+void SetImageStateRequest<I>::finish(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+template class librbd::mirror::snapshot::SetImageStateRequest<librbd::ImageCtx>;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_SET_IMAGE_STATE_REQUEST_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_SET_IMAGE_STATE_REQUEST_H
+
+#include "librbd/mirror/snapshot/Types.h"
+
+#include <map>
+#include <string>
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace mirror {
+namespace snapshot {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class SetImageStateRequest {
+public:
+ static SetImageStateRequest *create(ImageCtxT *image_ctx, uint64_t snap_id,
+ Context *on_finish) {
+ return new SetImageStateRequest(image_ctx, snap_id, on_finish);
+ }
+
+ SetImageStateRequest(ImageCtxT *image_ctx, uint64_t snap_id,
+ Context *on_finish)
+ : m_image_ctx(image_ctx), m_snap_id(snap_id), m_on_finish(on_finish) {
+ }
+
+ void send();
+
+private:
+ /**
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * GET_SNAP_LIMIT
+ * |
+ * v
+ * GET_METADATA (repeat until
+ * | all metadata read)
+ * v
+ * WRITE_IMAGE_STATE
+ * |
+ * v
+ * <finish>
+ *
+ * @endverbatim
+ */
+
+ ImageCtxT *m_image_ctx;
+ uint64_t m_snap_id;
+ Context *m_on_finish;
+
+ ImageState m_image_state;
+
+ bufferlist m_bl;
+ bufferlist m_state_bl;
+ std::string m_last_metadata_key;
+
+ void get_snap_limit();
+ void handle_get_snap_limit(int r);
+
+ void get_metadata();
+ void handle_get_metadata(int r);
+
+ void write_image_state();
+ void handle_write_image_state(int r);
+
+ void finish(int r);
+};
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+extern template class librbd::mirror::snapshot::SetImageStateRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_SET_IMAGE_STATE_REQUEST_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "common/Formatter.h"
+#include "include/encoding.h"
+#include "include/stringify.h"
+#include "librbd/mirror/snapshot/Types.h"
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+void ImageStateHeader::encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(object_count, bl);
+ ENCODE_FINISH(bl);
+}
+
+void ImageStateHeader::decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(object_count, bl);
+ DECODE_FINISH(bl);
+}
+
+void SnapState::encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(id, bl);
+ encode(snap_namespace, bl);
+ encode(name, bl);
+ encode(protection_status, bl);
+ ENCODE_FINISH(bl);
+}
+
+void SnapState::decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(id, bl);
+ decode(snap_namespace, bl);
+ decode(name, bl);
+ DECODE_FINISH(bl);
+}
+
+void SnapState::dump(Formatter *f) const {
+ f->dump_unsigned("id", id);
+ f->open_object_section("namespace");
+ snap_namespace.dump(f);
+ f->close_section();
+ f->dump_string("name", name);
+ f->dump_unsigned("protection_status", protection_status);
+}
+
+std::ostream& operator<<(std::ostream& os, const SnapState& snap_state) {
+ os << "[" << snap_state.id << " " << snap_state.snap_namespace << " "
+ << snap_state.name << " " << snap_state.protection_status << "]";
+ return os;
+}
+
+void ImageState::encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(name, bl);
+ encode(features, bl);
+ encode(snap_limit, bl);
+ encode(snapshots, bl);
+ encode(metadata, bl);
+ ENCODE_FINISH(bl);
+}
+
+void ImageState::decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(name, bl);
+ decode(features, bl);
+ decode(snap_limit, bl);
+ decode(snapshots, bl);
+ decode(metadata, bl);
+ DECODE_FINISH(bl);
+}
+
+void ImageState::dump(Formatter *f) const {
+ f->dump_string("name", name);
+ f->dump_unsigned("features", features);
+ f->dump_unsigned("snap_limit", snap_limit);
+ f->open_array_section("snapshots");
+ for (auto &[id, snap_state] : snapshots) {
+ f->open_object_section(stringify(id).c_str());
+ snap_state.dump(f);
+ f->close_section(); // snap_state
+ }
+ f->close_section(); // snapshots
+ f->open_object_section("metadata");
+ for (auto &it : metadata) {
+ f->dump_stream(it.first.c_str()) << it.second;
+ }
+ f->close_section(); // metadata
+}
+
+std::ostream& operator<<(std::ostream& os, const ImageState& image_state) {
+ os << "[" << image_state.name << " " << image_state.features << " "
+ << image_state.snap_limit << " " << image_state.snapshots.size()
+ << " " << image_state.metadata.size() << "]";
+ return os;
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_TYPES_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_TYPES_H
+
+#include "cls/rbd/cls_rbd_types.h"
+#include "include/buffer.h"
+#include "include/types.h"
+
+#include <map>
+#include <string>
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+struct ImageStateHeader {
+ uint32_t object_count = 0;
+
+ ImageStateHeader() {
+ }
+ ImageStateHeader(uint32_t object_count) : object_count(object_count) {
+ }
+
+ void encode(bufferlist &bl) const;
+ void decode(bufferlist::const_iterator &it);
+};
+
+WRITE_CLASS_ENCODER(ImageStateHeader);
+
+struct SnapState {
+ uint64_t id = CEPH_NOSNAP;
+ cls::rbd::SnapshotNamespace snap_namespace;
+ std::string name;
+ uint8_t protection_status = 0;
+
+ SnapState() {
+ }
+ SnapState(uint64_t id, const cls::rbd::SnapshotNamespace &snap_namespace,
+ const std::string &name, uint8_t protection_status)
+ : id(id), snap_namespace(snap_namespace), name(name),
+ protection_status(protection_status) {
+ }
+
+ bool operator==(const SnapState& rhs) const {
+ return id == rhs.id && snap_namespace == rhs.snap_namespace &&
+ name == rhs.name && protection_status == rhs.protection_status;
+ }
+
+ bool operator<(const SnapState& rhs) const {
+ if (id != rhs.id) {
+ return id < rhs.id;
+ }
+ if (snap_namespace != rhs.snap_namespace) {
+ return snap_namespace < rhs.snap_namespace;
+ }
+ if (name != rhs.name) {
+ return name < rhs.name;
+ }
+ return protection_status < rhs.protection_status;
+ }
+
+ void encode(bufferlist &bl) const;
+ void decode(bufferlist::const_iterator &it);
+ void dump(Formatter *f) const;
+};
+
+std::ostream& operator<<(std::ostream& os, const SnapState& snap_state);
+
+WRITE_CLASS_ENCODER(SnapState);
+
+struct ImageState {
+ std::string name;
+ uint64_t features = 0;
+ uint64_t snap_limit = 0;
+ std::map<uint64_t, SnapState> snapshots;
+ std::map<std::string, bufferlist> metadata;
+
+ ImageState() {
+ }
+ ImageState(const std::string &name, uint64_t features, uint64_t snap_limit,
+ const std::map<uint64_t, SnapState> &snapshots,
+ const std::map<std::string, bufferlist> &metadata)
+ : name(name), features(features), snap_limit(snap_limit),
+ snapshots(snapshots), metadata(metadata) {
+ }
+
+ bool operator==(const ImageState& rhs) const {
+ return name == rhs.name && features == rhs.features &&
+ snap_limit == rhs.snap_limit && snapshots == rhs.snapshots;
+ }
+
+ bool operator<(const ImageState& rhs) const {
+ if (name != rhs.name) {
+ return name < rhs.name;
+ }
+ if (features != rhs.features) {
+ return features < rhs.features;
+ }
+ if (snap_limit != rhs.snap_limit) {
+ return snap_limit < rhs.snap_limit;
+ }
+ return snapshots < rhs.snapshots;
+ }
+
+ void encode(bufferlist &bl) const;
+ void decode(bufferlist::const_iterator &it);
+ void dump(Formatter *f) const;
+};
+
+std::ostream& operator<<(std::ostream& os, const ImageState& image_state);
+
+WRITE_CLASS_ENCODER(ImageState);
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_TYPES_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_UTILS_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_UTILS_H
+
+#include "include/stringify.h"
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+namespace util {
+
+const std::string IMAGE_STATE_OBJECT_PREFIX = "rbd_mirror_snapshot.";
+
+template <typename ImageCtxT = librbd::ImageCtx>
+std::string image_state_object_name(ImageCtxT *image_ctx, uint64_t snap_id,
+ uint64_t index) {
+ return IMAGE_STATE_OBJECT_PREFIX + image_ctx->id + "." +
+ stringify(snap_id) + "." + stringify(index);
+}
+
+} // namespace util
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_UTILS_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/mirror/snapshot/WriteImageStateRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/Utils.h"
+#include "librbd/mirror/snapshot/Utils.h"
+
+#define dout_subsys ceph_subsys_rbd
+
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::mirror::snapshot::WriteImageStateRequest: " \
+ << this << " " << __func__ << ": "
+
+namespace librbd {
+namespace mirror {
+namespace snapshot {
+
+namespace {
+
+static size_t header_length() {
+ bufferlist bl;
+ ImageStateHeader header;
+
+ using ceph::encode;
+ encode(header, bl);
+
+ return bl.length();
+}
+
+}
+using librbd::util::create_rados_callback;
+
+template <typename I>
+WriteImageStateRequest<I>::WriteImageStateRequest(I *image_ctx,
+ uint64_t snap_id,
+ const ImageState &image_state,
+ Context *on_finish)
+ : m_image_ctx(image_ctx), m_snap_id(snap_id), m_image_state(image_state),
+ m_on_finish(on_finish), m_object_size(
+ 1 << image_ctx->config.template get_val<uint64_t>("rbd_default_order")) {
+ bufferlist bl;
+ encode(m_image_state, bl);
+
+ m_object_count = 1 + (header_length() + bl.length()) / m_object_size;
+ ImageStateHeader header(m_object_count);
+
+ encode(header, m_bl);
+ m_bl.claim_append(bl);
+}
+
+template <typename I>
+void WriteImageStateRequest<I>::send() {
+ write_object();
+}
+
+template <typename I>
+void WriteImageStateRequest<I>::write_object() {
+ CephContext *cct = m_image_ctx->cct;
+ ceph_assert(m_object_count > 0);
+
+ m_object_count--;
+
+ auto oid = util::image_state_object_name(m_image_ctx, m_snap_id,
+ m_object_count);
+ ldout(cct, 20) << oid << dendl;
+
+ size_t off = m_object_count * m_object_size;
+ size_t len = std::min(m_bl.length() - off, m_object_size);
+ bufferlist bl;
+ bl.substr_of(m_bl, off, len);
+
+ librados::ObjectWriteOperation op;
+ op.write_full(bl);
+
+ librados::AioCompletion *comp = create_rados_callback<
+ WriteImageStateRequest<I>,
+ &WriteImageStateRequest<I>::handle_write_object>(this);
+ int r = m_image_ctx->md_ctx.aio_operate(oid, comp, &op);
+ ceph_assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+void WriteImageStateRequest<I>::handle_write_object(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "failed to write object: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ if (m_object_count == 0) {
+ finish(0);
+ return;
+ }
+
+ write_object();
+}
+
+template <typename I>
+void WriteImageStateRequest<I>::finish(int r) {
+ CephContext *cct = m_image_ctx->cct;
+ ldout(cct, 20) << "r=" << r << dendl;
+
+ m_on_finish->complete(r);
+ delete this;
+}
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+template class librbd::mirror::snapshot::WriteImageStateRequest<librbd::ImageCtx>;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIRROR_SNAPSHOT_WRITE_IMAGE_STATE_REQUEST_H
+#define CEPH_LIBRBD_MIRROR_SNAPSHOT_WRITE_IMAGE_STATE_REQUEST_H
+
+#include "librbd/mirror/snapshot/Types.h"
+
+#include <map>
+#include <string>
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace mirror {
+namespace snapshot {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class WriteImageStateRequest {
+public:
+ static WriteImageStateRequest *create(ImageCtxT *image_ctx, uint64_t snap_id,
+ const ImageState &image_state,
+ Context *on_finish) {
+ return new WriteImageStateRequest(image_ctx, snap_id, image_state,
+ on_finish);
+ }
+
+ WriteImageStateRequest(ImageCtxT *image_ctx, uint64_t snap_id,
+ const ImageState &image_state, Context *on_finish);
+
+ void send();
+
+private:
+ /**
+ * @verbatim
+ *
+ * <start>
+ * |
+ * v
+ * WRITE_OBJECT (repeat for
+ * | every object)
+ * v
+ * <finish>
+ *
+ * @endverbatim
+ */
+
+ ImageCtxT *m_image_ctx;
+ uint64_t m_snap_id;
+ ImageState m_image_state;
+ Context *m_on_finish;
+
+ bufferlist m_bl;
+
+ const size_t m_object_size;
+ size_t m_object_count = 0;
+
+ void write_object();
+ void handle_write_object(int r);
+
+ void finish(int r);
+};
+
+} // namespace snapshot
+} // namespace mirror
+} // namespace librbd
+
+extern template class librbd::mirror::snapshot::WriteImageStateRequest<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_MIRROR_SNAPSHOT_WRITE_IMAGE_STATE_REQUEST_H
#include "librbd/io/ImageRequest.h"
#include "librbd/io/ImageRequestWQ.h"
#include "librbd/journal/Types.h"
+#include "librbd/mirror/snapshot/GetImageStateRequest.h"
+#include "librbd/mirror/snapshot/RemoveImageStateRequest.h"
+#include "librbd/mirror/snapshot/SetImageStateRequest.h"
#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
#include "journal/Journaler.h"
#include "journal/Settings.h"
ASSERT_EQ(0, m_rbd.mirror_peer_site_remove(m_ioctx, peer2_uuid));
ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED));
}
+
+TEST_F(TestMirroring, SnapshotImageState)
+{
+ REQUIRE_FORMAT_V2();
+
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+ features &= ~RBD_FEATURE_JOURNALING;
+ int order = 20;
+ ASSERT_EQ(0, m_rbd.create2(m_ioctx, image_name.c_str(), 4096, features,
+ &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, image_name.c_str()));
+ ASSERT_EQ(0, image.snap_create("snap"));
+
+ auto ictx = new librbd::ImageCtx(image_name, "", nullptr, m_ioctx, false);
+ ASSERT_EQ(0, ictx->state->open(0));
+ BOOST_SCOPE_EXIT(&ictx) {
+ if (ictx != nullptr) {
+ ictx->state->close();
+ }
+ } BOOST_SCOPE_EXIT_END;
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::SetImageStateRequest<>::create(
+ ictx, 123, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ librbd::mirror::snapshot::ImageState image_state;
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::GetImageStateRequest<>::create(
+ ictx, 123, &image_state, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ ASSERT_EQ(image_name, image_state.name);
+ ASSERT_EQ(0, image.features(&features));
+ ASSERT_EQ(features, image_state.features);
+ ASSERT_EQ(1U, image_state.snapshots.size());
+ ASSERT_EQ("snap", image_state.snapshots.begin()->second.name);
+ ASSERT_TRUE(image_state.metadata.empty());
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::RemoveImageStateRequest<>::create(
+ ictx, 123, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ // test storing "large" image state in multiple objects
+
+ ASSERT_EQ(0, ictx->config.set_val("rbd_default_order", "8"));
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(0, image.metadata_set(stringify(i), std::string(1024, 'A' + i)));
+ }
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::SetImageStateRequest<>::create(
+ ictx, 123, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::GetImageStateRequest<>::create(
+ ictx, 123, &image_state, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ ASSERT_EQ(image_name, image_state.name);
+ ASSERT_EQ(features, image_state.features);
+ ASSERT_EQ(10U, image_state.metadata.size());
+ for (int i = 0; i < 10; i++) {
+ auto &bl = image_state.metadata[stringify(i)];
+ ASSERT_EQ(0, strncmp(std::string(1024, 'A' + i).c_str(), bl.c_str(),
+ bl.length()));
+ }
+
+ {
+ C_SaferCond cond;
+ auto req = librbd::mirror::snapshot::RemoveImageStateRequest<>::create(
+ ictx, 123, &cond);
+ req->send();
+ ASSERT_EQ(0, cond.wait());
+ }
+
+ ASSERT_EQ(0, ictx->state->close());
+ ictx = nullptr;
+
+ ASSERT_EQ(0, image.snap_remove("snap"));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.remove(m_ioctx, image_name.c_str()));
+}