#include "common/errno.h"
#include "librbd/Utils.h"
#include "librbd/deep_copy/Utils.h"
+#include "librbd/image/CloseRequest.h"
+#include "librbd/image/OpenRequest.h"
+#include "librbd/image/SetSnapRequest.h"
#include "osdc/Striper.h"
#define dout_subsys ceph_subsys_rbd
namespace librbd {
namespace deep_copy {
+using librbd::util::create_context_callback;
using librbd::util::unique_lock_name;
template <typename I>
return;
}
- send_object_copies();
+ send_open_parent();
}
template <typename I>
m_canceled = true;
}
+template <typename I>
+void ImageCopyRequest<I>::send_open_parent() {
+ {
+ RWLock::RLocker snap_locker(m_src_image_ctx->snap_lock);
+ RWLock::RLocker parent_locker(m_src_image_ctx->parent_lock);
+
+ auto snap_id = m_snap_map.begin()->first;
+ auto parent_info = m_src_image_ctx->get_parent_info(snap_id);
+ if (parent_info == nullptr) {
+ ldout(m_cct, 20) << "could not find parent info for snap id " << snap_id
+ << dendl;
+ } else {
+ m_parent_spec = parent_info->spec;
+ }
+ }
+
+ if (m_parent_spec.pool_id == -1) {
+ send_object_copies();
+ return;
+ }
+
+ ldout(m_cct, 20) << "pool_id=" << m_parent_spec.pool_id << ", image_id="
+ << m_parent_spec.image_id << ", snap_id="
+ << m_parent_spec.snap_id << dendl;
+
+ librados::Rados rados(m_src_image_ctx->md_ctx);
+ librados::IoCtx parent_io_ctx;
+ int r = rados.ioctx_create2(m_parent_spec.pool_id, parent_io_ctx);
+ if (r < 0) {
+ lderr(m_cct) << "failed to access parent pool (id=" << m_parent_spec.pool_id
+ << "): " << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
+ m_src_parent_image_ctx = I::create("", m_parent_spec.image_id, nullptr, parent_io_ctx, true);
+
+ auto ctx = create_context_callback<
+ ImageCopyRequest<I>, &ImageCopyRequest<I>::handle_open_parent>(this);
+
+ auto req = image::OpenRequest<I>::create(m_src_parent_image_ctx, false, ctx);
+ req->send();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::handle_open_parent(int r) {
+ ldout(m_cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to open parent: " << cpp_strerror(r) << dendl;
+ m_src_parent_image_ctx->destroy();
+ m_src_parent_image_ctx = nullptr;
+ finish(r);
+ return;
+ }
+
+ send_set_parent_snap();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::send_set_parent_snap() {
+ ldout(m_cct, 20) << dendl;
+
+ auto ctx = create_context_callback<
+ ImageCopyRequest<I>, &ImageCopyRequest<I>::handle_set_parent_snap>(this);
+ auto req = image::SetSnapRequest<I>::create(*m_src_parent_image_ctx,
+ m_parent_spec.snap_id, ctx);
+ req->send();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::handle_set_parent_snap(int r) {
+ ldout(m_cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to set parent snap: " << cpp_strerror(r) << dendl;
+ m_ret_val = r;
+ send_close_parent();
+ return;
+ }
+
+ send_object_copies();
+}
+
template <typename I>
void ImageCopyRequest<I>::send_object_copies() {
m_object_no = 0;
}
if (complete) {
- finish(m_ret_val);
+ send_close_parent();
}
}
handle_object_copy(ono, r);
});
ObjectCopyRequest<I> *req = ObjectCopyRequest<I>::create(
- m_src_image_ctx, m_dst_image_ctx, m_snap_map, ono, ctx);
+ m_src_image_ctx, m_src_parent_image_ctx, m_dst_image_ctx, m_snap_map, ono,
+ ctx);
req->send();
}
}
if (complete) {
+ send_close_parent();
+ }
+}
+
+template <typename I>
+void ImageCopyRequest<I>::send_close_parent() {
+ if (m_src_parent_image_ctx == nullptr) {
finish(m_ret_val);
+ return;
}
+
+ ldout(m_cct, 20) << dendl;
+
+ auto ctx = create_context_callback<
+ ImageCopyRequest<I>, &ImageCopyRequest<I>::handle_close_parent>(this);
+ auto req = image::CloseRequest<I>::create(m_src_parent_image_ctx, ctx);
+ req->send();
+}
+
+template <typename I>
+void ImageCopyRequest<I>::handle_close_parent(int r) {
+ ldout(m_cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to close parent: " << cpp_strerror(r) << dendl;
+ if (m_ret_val == 0) {
+ m_ret_val = r;
+ }
+ }
+
+ m_src_parent_image_ctx->destroy();
+ m_src_parent_image_ctx = nullptr;
+
+ finish(m_ret_val);
}
template <typename I>
/**
* @verbatim
*
- * <start> . . . . .
+ * <start>
+ * |
+ * v
+ * OPEN_PARENT (skip if not needed)
+ * |
+ * v
+ * SET_PARENT_SNAP (skip if not needed)
+ * |
+ * | . . . . .
* | . . (parallel execution of
* v v . multiple objects at once)
* COPY_OBJECT . . . .
* |
* v
+ * CLOSE_PARENT (skip if not needed)
+ * |
+ * v
* <finish>
*
* @endverbatim
bool m_updating_progress = false;
SnapMap m_snap_map;
int m_ret_val = 0;
+ ParentSpec m_parent_spec;
+ ImageCtxT *m_src_parent_image_ctx = nullptr;
+
+ void send_open_parent();
+ void handle_open_parent(int r);
+
+ void send_set_parent_snap();
+ void handle_set_parent_snap(int r);
void send_object_copies();
void send_next_object_copy();
void handle_object_copy(uint64_t object_no, int r);
+ void send_close_parent();
+ void handle_close_parent(int r);
+
void finish(int r);
};
#include "librbd/ExclusiveLock.h"
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageRequest.h"
+#include "librbd/io/ReadResult.h"
#include "osdc/Striper.h"
#define dout_subsys ceph_subsys_rbd
using librbd::util::create_rados_callback;
template <typename I>
-ObjectCopyRequest<I>::ObjectCopyRequest(I *src_image_ctx, I *dst_image_ctx,
+ObjectCopyRequest<I>::ObjectCopyRequest(I *src_image_ctx,
+ I *src_parent_image_ctx,
+ I *dst_image_ctx,
const SnapMap &snap_map,
uint64_t dst_object_number,
Context *on_finish)
- : m_src_image_ctx(src_image_ctx), m_dst_image_ctx(dst_image_ctx),
- m_cct(dst_image_ctx->cct), m_snap_map(snap_map),
- m_dst_object_number(dst_object_number), m_on_finish(on_finish) {
+ : m_src_image_ctx(src_image_ctx),
+ m_src_parent_image_ctx(src_parent_image_ctx),
+ m_dst_image_ctx(dst_image_ctx), m_cct(dst_image_ctx->cct),
+ m_snap_map(snap_map), m_dst_object_number(dst_object_number),
+ m_on_finish(on_finish) {
assert(!m_snap_map.empty());
m_src_io_ctx.dup(m_src_image_ctx->data_ctx);
ldout(m_cct, 20) << "r=" << r << dendl;
- if (r == -ENOENT) {
- r = 0;
- }
-
- if (r < 0) {
+ if (r < 0 && r != -ENOENT) {
lderr(m_cct) << "failed to list snaps: " << cpp_strerror(r) << dendl;
finish(r);
return;
m_retry_snap_set = {};
}
- compute_read_ops();
+ if (r == -ENOENT) {
+ for (auto &it : m_src_object_extents) {
+ auto &e = it.second;
+ if (e.object_no == m_src_ono) {
+ e.noent = true;
+ }
+ }
+ m_read_ops = {};
+ m_read_snaps = {};
+ m_zero_interval = {};
+ } else {
+ compute_read_ops();
+ }
send_read_object();
}
}
// all objects have been read
-
- compute_zero_ops();
-
- if (m_write_ops.empty()) {
- // nothing to copy
- finish(0);
- return;
- }
-
- send_write_object();
+ send_read_from_parent();
return;
}
send_read_object();
}
+template <typename I>
+void ObjectCopyRequest<I>::send_read_from_parent() {
+ io::Extents image_extents;
+ compute_read_from_parent_ops(&image_extents);
+ if (image_extents.empty()) {
+ handle_read_from_parent(0);
+ return;
+ }
+
+ ldout(m_cct, 20) << dendl;
+
+ assert(m_src_parent_image_ctx != nullptr);
+
+ auto ctx = create_context_callback<
+ ObjectCopyRequest<I>, &ObjectCopyRequest<I>::handle_read_from_parent>(this);
+ auto comp = io::AioCompletion::create_and_start(
+ ctx, util::get_image_ctx(m_src_image_ctx), io::AIO_TYPE_READ);
+ ldout(m_cct, 20) << "completion " << comp << ", extents " << image_extents
+ << dendl;
+ io::ImageRequest<I>::aio_read(m_src_parent_image_ctx, comp,
+ std::move(image_extents),
+ io::ReadResult{&m_read_from_parent_data}, 0,
+ ZTracer::Trace());
+}
+
+template <typename I>
+void ObjectCopyRequest<I>::handle_read_from_parent(int r) {
+ ldout(m_cct, 20) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(m_cct) << "failed to read from parent: " << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
+ if (!m_read_ops.empty()) {
+ assert(m_read_ops.size() == 1);
+ auto src_snap_seq = m_read_ops.begin()->first.first;
+ auto ©_ops = m_read_ops.begin()->second;
+ uint64_t offset = 0;
+ for (auto it = copy_ops.begin(); it != copy_ops.end(); ) {
+ it->out_bl.substr_of(m_read_from_parent_data, offset, it->length);
+ offset += it->length;
+ if (it->out_bl.is_zero()) {
+ m_zero_interval[src_snap_seq].insert(it->dst_offset, it->length);
+ it = copy_ops.erase(it);
+ } else {
+ it++;
+ }
+ }
+ merge_write_ops();
+ }
+
+ compute_zero_ops();
+
+ if (m_write_ops.empty()) {
+ // nothing to copy
+ finish(0);
+ return;
+ }
+
+ send_write_object();
+ return;
+}
+
template <typename I>
void ObjectCopyRequest<I>::send_write_object() {
assert(!m_write_ops.empty());
}
}
+template <typename I>
+void ObjectCopyRequest<I>::compute_read_from_parent_ops(
+ io::Extents *parent_image_extents) {
+ m_read_ops = {};
+ m_zero_interval = {};
+ parent_image_extents->clear();
+
+ if (m_src_parent_image_ctx == nullptr) {
+ ldout(m_cct, 20) << "no parent" << dendl;
+ return;
+ }
+
+ size_t noent_count = 0;
+ for (auto &it : m_src_object_extents) {
+ if (it.second.noent) {
+ noent_count++;
+ }
+ }
+
+ if (noent_count == 0) {
+ ldout(m_cct, 20) << "no extents need read from parent" << dendl;
+ return;
+ }
+
+ if (noent_count == m_src_object_extents.size()) {
+ ldout(m_cct, 20) << "reading all extents skipped when no flatten"
+ << dendl;
+ return;
+ }
+
+ ldout(m_cct, 20) << dendl;
+
+ auto src_snap_seq = m_snap_map.begin()->first;
+
+ RWLock::RLocker snap_locker(m_src_image_ctx->snap_lock);
+ RWLock::RLocker parent_locker(m_src_image_ctx->parent_lock);
+
+ uint64_t parent_overlap;
+ int r = m_src_image_ctx->get_parent_overlap(src_snap_seq, &parent_overlap);
+ if (r < 0) {
+ ldout(m_cct, 5) << "failed getting parent overlap for snap_id: "
+ << src_snap_seq << ": " << cpp_strerror(r) << dendl;
+ return;
+ }
+ if (parent_overlap == 0) {
+ ldout(m_cct, 20) << "no parent overlap" << dendl;
+ return;
+ }
+
+ for (auto &it : m_src_object_extents) {
+ auto dst_object_offset = it.first;
+ auto &e = it.second;
+
+ if (!e.noent) {
+ continue;
+ }
+
+ std::vector<std::pair<uint64_t, uint64_t>> image_extents;
+ Striper::extent_to_file(m_cct, &m_src_image_ctx->layout, e.object_no,
+ e.offset, e.length, image_extents);
+
+ uint64_t overlap = m_src_image_ctx->prune_parent_extents(image_extents,
+ parent_overlap);
+ if (overlap == 0) {
+ ldout(m_cct, 20) << "no parent overlap for object_no " << e.object_no
+ << " extent " << e.offset << "~" << e.length << dendl;
+ continue;
+ }
+
+ ldout(m_cct, 20) << "object_no " << e.object_no << " extent " << e.offset
+ << "~" << e.length << " overlap " << parent_overlap
+ << " parent extents " << image_extents << dendl;
+
+ assert(image_extents.size() == 1);
+
+ auto src_image_offset = image_extents.begin()->first;
+ auto length = image_extents.begin()->second;
+ m_read_ops[{src_snap_seq, 0}].emplace_back(COPY_OP_TYPE_WRITE, e.offset,
+ dst_object_offset, length);
+ m_read_ops[{src_snap_seq, 0}].rbegin()->src_extent_map[e.offset] = length;
+ parent_image_extents->emplace_back(src_image_offset, length);
+ }
+
+ if (!parent_image_extents->empty()) {
+ m_dst_object_state[src_snap_seq] = OBJECT_EXISTS;
+ }
+}
+
template <typename I>
void ObjectCopyRequest<I>::merge_write_ops() {
ldout(m_cct, 20) << dendl;
#include "common/snap_types.h"
#include "librbd/ImageCtx.h"
#include "librbd/deep_copy/Types.h"
+#include "librbd/io/Types.h"
#include <list>
#include <map>
#include <string>
class ObjectCopyRequest {
public:
static ObjectCopyRequest* create(ImageCtxT *src_image_ctx,
+ ImageCtxT *src_parent_image_ctx,
ImageCtxT *dst_image_ctx,
const SnapMap &snap_map,
- uint64_t object_number, Context *on_finish) {
- return new ObjectCopyRequest(src_image_ctx, dst_image_ctx, snap_map,
- object_number, on_finish);
+ uint64_t object_number,
+ Context *on_finish) {
+ return new ObjectCopyRequest(src_image_ctx, src_parent_image_ctx,
+ dst_image_ctx, snap_map, object_number,
+ on_finish);
}
- ObjectCopyRequest(ImageCtxT *src_image_ctx, ImageCtxT *dst_image_ctx,
- const SnapMap &snap_map, uint64_t object_number,
- Context *on_finish);
+ ObjectCopyRequest(ImageCtxT *src_image_ctx, ImageCtxT *src_parent_image_ctx,
+ ImageCtxT *dst_image_ctx, const SnapMap &snap_map,
+ uint64_t object_number, Context *on_finish);
void send();
* READ_OBJECT ---------/ |
* | | |
* | \----------------------/
+ * v
+ * READ_FROM_PARENT (skip if not needed)
* |
* | /-----------\
* | | | (repeat for each snapshot)
uint64_t object_no = 0;
uint64_t offset = 0;
uint64_t length = 0;
+ bool noent = false;
SrcObjectExtent() {
}
typedef std::map<librados::snap_t, std::map<uint64_t, uint64_t>> SnapObjectSizes;
ImageCtxT *m_src_image_ctx;
+ ImageCtxT *m_src_parent_image_ctx;
ImageCtxT *m_dst_image_ctx;
CephContext *m_cct;
const SnapMap &m_snap_map;
std::map<librados::snap_t, interval_set<uint64_t>> m_zero_interval;
std::map<librados::snap_t, interval_set<uint64_t>> m_dst_zero_interval;
std::map<librados::snap_t, uint8_t> m_dst_object_state;
+ bufferlist m_read_from_parent_data;
void send_list_snaps();
void handle_list_snaps(int r);
void send_read_object();
void handle_read_object(int r);
+ void send_read_from_parent();
+ void handle_read_from_parent(int r);
+
void send_write_object();
void handle_write_object(int r);
void compute_src_object_extents();
void compute_read_ops();
+ void compute_read_from_parent_ops(io::Extents *image_extents);
void merge_write_ops();
void compute_zero_ops();
#include "librbd/Operations.h"
#include "librbd/deep_copy/ImageCopyRequest.h"
#include "librbd/deep_copy/ObjectCopyRequest.h"
+#include "librbd/image/CloseRequest.h"
+#include "librbd/image/OpenRequest.h"
+#include "librbd/image/SetSnapRequest.h"
#include "librbd/internal.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
#include "test/librbd/mock/MockImageCtx.h"
namespace {
struct MockTestImageCtx : public librbd::MockImageCtx {
- MockTestImageCtx(librbd::ImageCtx &image_ctx)
+ static MockTestImageCtx* s_instance;
+ static MockTestImageCtx* create(const std::string &image_name,
+ const std::string &image_id,
+ const char *snap, librados::IoCtx& p,
+ bool read_only) {
+ assert(s_instance != nullptr);
+ return s_instance;
+ }
+
+ explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
: librbd::MockImageCtx(image_ctx) {
+ s_instance = this;
}
+
+ MOCK_METHOD0(destroy, void());
};
+MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
+
} // anonymous namespace
namespace deep_copy {
static ObjectCopyRequest* s_instance;
static ObjectCopyRequest* create(
librbd::MockTestImageCtx *src_image_ctx,
+ librbd::MockTestImageCtx *src_parent_image_ctx,
librbd::MockTestImageCtx *dst_image_ctx, const SnapMap &snap_map,
uint64_t object_number, Context *on_finish) {
assert(s_instance != nullptr);
ObjectCopyRequest<librbd::MockTestImageCtx>* ObjectCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
} // namespace deep_copy
+
+namespace image {
+
+template <>
+struct CloseRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static CloseRequest* s_instance;
+ static CloseRequest* create(MockTestImageCtx *image_ctx, Context *on_finish) {
+ assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ CloseRequest() {
+ s_instance = this;
+ }
+};
+
+CloseRequest<MockTestImageCtx>* CloseRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct OpenRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static OpenRequest* s_instance;
+ static OpenRequest* create(MockTestImageCtx *image_ctx,
+ bool skip_open_parent, Context *on_finish) {
+ assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ OpenRequest() {
+ s_instance = this;
+ }
+};
+
+OpenRequest<MockTestImageCtx>* OpenRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct SetSnapRequest<MockTestImageCtx> {
+ Context* on_finish = nullptr;
+ static SetSnapRequest* s_instance;
+ static SetSnapRequest* create(MockTestImageCtx &image_ctx, uint64_t snap_id,
+ Context *on_finish) {
+ assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ MOCK_METHOD0(send, void());
+
+ SetSnapRequest() {
+ s_instance = this;
+ }
+};
+
+SetSnapRequest<MockTestImageCtx>* SetSnapRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
} // namespace librbd
// template definitions
.WillOnce(Return(size)).RetiresOnSaturation();
}
+ void expect_get_parent_info(librbd::MockTestImageCtx &mock_image_ctx) {
+ EXPECT_CALL(mock_image_ctx, get_parent_info(_)).WillOnce(Return(nullptr));
+ }
+
void expect_object_copy_send(MockObjectCopyRequest &mock_object_copy_request) {
EXPECT_CALL(mock_object_copy_request, send());
}
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ expect_get_parent_info(mock_src_image_ctx);
expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
expect_get_image_size(mock_src_image_ctx, 0);
expect_object_copy_send(mock_object_copy_request);
librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
MockObjectCopyRequest mock_object_copy_request;
+ expect_get_parent_info(mock_src_image_ctx);
expect_get_image_size(mock_src_image_ctx,
object_count * (1 << m_src_image_ctx->order));
expect_get_image_size(mock_src_image_ctx, 0);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ expect_get_parent_info(mock_src_image_ctx);
expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
expect_get_image_size(mock_src_image_ctx, 0);
expect_get_image_size(mock_src_image_ctx, 0);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ expect_get_parent_info(mock_src_image_ctx);
expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order));
expect_get_image_size(mock_src_image_ctx, 0);
expect_object_copy_send(mock_object_copy_request);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ expect_get_parent_info(mock_src_image_ctx);
expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
expect_get_image_size(mock_src_image_ctx, 0);
expect_object_copy_send(mock_object_copy_request);
MockObjectCopyRequest mock_object_copy_request;
InSequence seq;
+ expect_get_parent_info(mock_src_image_ctx);
expect_get_image_size(mock_src_image_ctx, 6 * (1 << m_src_image_ctx->order));
expect_get_image_size(mock_src_image_ctx, m_image_size);
#include "librbd/Operations.h"
#include "librbd/api/Image.h"
#include "librbd/deep_copy/ObjectCopyRequest.h"
+#include "librbd/io/ImageRequest.h"
#include "librbd/io/ImageRequestWQ.h"
#include "librbd/io/ReadResult.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
};
} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx* get_image_ctx(MockTestImageCtx* image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+
+namespace io {
+
+template <>
+struct ImageRequest<MockTestImageCtx> {
+ static ImageRequest *s_instance;
+
+ static void aio_read(MockTestImageCtx *ictx, AioCompletion *c,
+ Extents &&image_extents, ReadResult &&read_result,
+ int op_flags, const ZTracer::Trace &parent_trace) {
+ assert(s_instance != nullptr);
+ s_instance->aio_read(c, image_extents);
+ }
+ MOCK_METHOD2(aio_read, void(AioCompletion *, const Extents&));
+};
+
+ImageRequest<MockTestImageCtx> *ImageRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace io
+
} // namespace librbd
// template definitions
librbd::MockTestImageCtx &mock_src_image_ctx,
librbd::MockTestImageCtx &mock_dst_image_ctx, Context *on_finish) {
expect_get_object_name(mock_dst_image_ctx);
- return new MockObjectCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx,
- m_snap_map, 0, on_finish);
+ return new MockObjectCopyRequest(&mock_src_image_ctx, nullptr,
+ &mock_dst_image_ctx, m_snap_map, 0,
+ on_finish);
}
void expect_set_snap_read(librados::MockTestMemIoCtxImpl &mock_io_ctx,
cls::rbd::SnapshotNamespace *out_snap_namespace));
MOCK_CONST_METHOD2(get_parent_spec, int(librados::snap_t in_snap_id,
ParentSpec *pspec));
+ MOCK_CONST_METHOD1(get_parent_info, const ParentInfo*(librados::snap_t));
MOCK_CONST_METHOD2(get_parent_overlap, int(librados::snap_t in_snap_id,
uint64_t *overlap));
MOCK_CONST_METHOD2(prune_parent_extents, uint64_t(vector<pair<uint64_t,uint64_t> >& ,