librbd: API for image migration

Signed-off-by: Mykola Golub <mgolub@suse.com>
This commit is contained in:
Mykola Golub 2018-02-24 11:43:12 +02:00 committed by Jason Dillaman
parent 813e5f28cf
commit 119c7ea66b
40 changed files with 3941 additions and 77 deletions

View File

@ -230,6 +230,26 @@ typedef struct {
uint64_t cookie;
} rbd_image_watcher_t;
typedef enum {
RBD_IMAGE_MIGRATION_STATE_UNKNOWN = -1,
RBD_IMAGE_MIGRATION_STATE_ERROR = 0,
RBD_IMAGE_MIGRATION_STATE_PREPARING = 1,
RBD_IMAGE_MIGRATION_STATE_PREPARED = 2,
RBD_IMAGE_MIGRATION_STATE_EXECUTING = 3,
RBD_IMAGE_MIGRATION_STATE_EXECUTED = 4,
} rbd_image_migration_state_t;
typedef struct {
int64_t source_pool_id;
char *source_image_name;
char *source_image_id;
int64_t dest_pool_id;
char *dest_image_name;
char *dest_image_id;
rbd_image_migration_state_t state;
char *state_description;
} rbd_image_migration_status_t;
CEPH_RBD_API void rbd_image_options_create(rbd_image_options_t* opts);
CEPH_RBD_API void rbd_image_options_destroy(rbd_image_options_t opts);
CEPH_RBD_API int rbd_image_options_set_string(rbd_image_options_t opts,
@ -309,6 +329,37 @@ CEPH_RBD_API int rbd_trash_remove_with_progress(rados_ioctx_t io, const char *id
CEPH_RBD_API int rbd_trash_restore(rados_ioctx_t io, const char *id,
const char *name);
/* migration */
CEPH_RBD_API int rbd_migration_prepare(rados_ioctx_t ioctx,
const char *image_name,
rados_ioctx_t dest_ioctx,
const char *dest_image_name,
rbd_image_options_t opts);
CEPH_RBD_API int rbd_migration_execute(rados_ioctx_t ioctx,
const char *image_name);
CEPH_RBD_API int rbd_migration_execute_with_progress(rados_ioctx_t ioctx,
const char *image_name,
librbd_progress_fn_t cb,
void *cbdata);
CEPH_RBD_API int rbd_migration_abort(rados_ioctx_t ioctx,
const char *image_name);
CEPH_RBD_API int rbd_migration_abort_with_progress(rados_ioctx_t ioctx,
const char *image_name,
librbd_progress_fn_t cb,
void *cbdata);
CEPH_RBD_API int rbd_migration_commit(rados_ioctx_t ioctx,
const char *image_name);
CEPH_RBD_API int rbd_migration_commit_with_progress(rados_ioctx_t ioctx,
const char *image_name,
librbd_progress_fn_t cb,
void *cbdata);
CEPH_RBD_API int rbd_migration_status(rados_ioctx_t ioctx,
const char *image_name,
rbd_image_migration_status_t *status,
size_t status_size);
CEPH_RBD_API void rbd_migration_status_cleanup(
rbd_image_migration_status_t *status);
/* pool mirroring */
CEPH_RBD_API int rbd_mirror_mode_get(rados_ioctx_t io_ctx,
rbd_mirror_mode_t *mirror_mode);

View File

@ -128,6 +128,19 @@ namespace librbd {
uint64_t cookie;
} image_watcher_t;
typedef rbd_image_migration_state_t image_migration_state_t;
typedef struct {
int64_t source_pool_id;
std::string source_image_name;
std::string source_image_id;
int64_t dest_pool_id;
std::string dest_image_name;
std::string dest_image_id;
image_migration_state_t state;
std::string state_description;
} image_migration_status_t;
class CEPH_RBD_API RBD
{
public:
@ -195,6 +208,22 @@ public:
bool force, ProgressContext &pctx);
int trash_restore(IoCtx &io_ctx, const char *id, const char *name);
// Migration
int migration_prepare(IoCtx& io_ctx, const char *image_name,
IoCtx& dest_io_ctx, const char *dest_image_name,
ImageOptions& opts);
int migration_execute(IoCtx& io_ctx, const char *image_name);
int migration_execute_with_progress(IoCtx& io_ctx, const char *image_name,
ProgressContext &prog_ctx);
int migration_abort(IoCtx& io_ctx, const char *image_name);
int migration_abort_with_progress(IoCtx& io_ctx, const char *image_name,
ProgressContext &prog_ctx);
int migration_commit(IoCtx& io_ctx, const char *image_name);
int migration_commit_with_progress(IoCtx& io_ctx, const char *image_name,
ProgressContext &prog_ctx);
int migration_status(IoCtx& io_ctx, const char *image_name,
image_migration_status_t *status, size_t status_size);
// RBD pool mirroring support functions
int mirror_mode_get(IoCtx& io_ctx, rbd_mirror_mode_t *mirror_mode);
int mirror_mode_set(IoCtx& io_ctx, rbd_mirror_mode_t mirror_mode);

View File

@ -99,6 +99,7 @@
#define RBD_CRYPT_NONE 0
#define RBD_HEADER_TEXT "<<< Rados Block Device Image >>>\n"
#define RBD_MIGRATE_HEADER_TEXT "<<< Migrating RBD Image >>>\n"
#define RBD_HEADER_SIGNATURE "RBD"
#define RBD_HEADER_VERSION "001.005"

View File

@ -27,6 +27,7 @@ set(librbd_internal_srcs
api/DiffIterate.cc
api/Group.cc
api/Image.cc
api/Migration.cc
api/Mirror.cc
api/Namespace.cc
api/Snapshot.cc
@ -108,6 +109,7 @@ set(librbd_internal_srcs
operation/FlattenRequest.cc
operation/MetadataRemoveRequest.cc
operation/MetadataSetRequest.cc
operation/MigrateRequest.cc
operation/ObjectMapIterate.cc
operation/RebuildObjectMapRequest.cc
operation/RenameRequest.cc

View File

@ -122,6 +122,7 @@ namespace librbd {
ParentInfo parent_md;
ImageCtx *parent;
ImageCtx *child = nullptr;
MigrationInfo migration_info;
cls::rbd::GroupSpec group_spec;
uint64_t stripe_unit, stripe_count;
uint64_t flags;
@ -158,6 +159,8 @@ namespace librbd {
ContextWQ *op_work_queue;
bool ignore_migrating = false;
// Configuration
static const string METADATA_CONF_PREFIX;
bool non_blocking_aio;

View File

@ -281,6 +281,20 @@ void ImageWatcher<I>::notify_update_features(uint64_t features, bool enabled,
notify_lock_owner(UpdateFeaturesPayload(features, enabled), on_finish);
}
template <typename I>
void ImageWatcher<I>::notify_migrate(uint64_t request_id,
ProgressContext &prog_ctx,
Context *on_finish) {
assert(m_image_ctx.owner_lock.is_locked());
assert(m_image_ctx.exclusive_lock &&
!m_image_ctx.exclusive_lock->is_lock_owner());
AsyncRequestId async_request_id(get_client_id(), request_id);
notify_async_request(async_request_id, MigratePayload(async_request_id),
prog_ctx, on_finish);
}
template <typename I>
void ImageWatcher<I>::notify_header_update(Context *on_finish) {
ldout(m_image_ctx.cct, 10) << this << ": " << __func__ << dendl;
@ -912,6 +926,33 @@ bool ImageWatcher<I>::handle_payload(const UpdateFeaturesPayload& payload,
return true;
}
template <typename I>
bool ImageWatcher<I>::handle_payload(const MigratePayload &payload,
C_NotifyAck *ack_ctx) {
RWLock::RLocker l(m_image_ctx.owner_lock);
if (m_image_ctx.exclusive_lock != nullptr) {
int r;
if (m_image_ctx.exclusive_lock->accept_requests(&r)) {
bool new_request;
Context *ctx;
ProgressContext *prog_ctx;
r = prepare_async_request(payload.async_request_id, &new_request,
&ctx, &prog_ctx);
if (r == 0 && new_request) {
ldout(m_image_ctx.cct, 10) << this << " remote migrate request: "
<< payload.async_request_id << dendl;
m_image_ctx.operations->execute_migrate(*prog_ctx, ctx);
}
encode(ResponseMessage(r), ack_ctx->out);
} else if (r < 0) {
encode(ResponseMessage(r), ack_ctx->out);
}
}
return true;
}
template <typename I>
bool ImageWatcher<I>::handle_payload(const UnknownPayload &payload,
C_NotifyAck *ack_ctx) {

View File

@ -65,6 +65,9 @@ public:
void notify_update_features(uint64_t features, bool enabled,
Context *on_finish);
void notify_migrate(uint64_t request_id, ProgressContext &prog_ctx,
Context *on_finish);
void notify_acquired_lock();
void notify_released_lock();
void notify_request_lock();
@ -233,6 +236,8 @@ private:
C_NotifyAck *ctx);
bool handle_payload(const watch_notify::UpdateFeaturesPayload& payload,
C_NotifyAck *ctx);
bool handle_payload(const watch_notify::MigratePayload& payload,
C_NotifyAck *ctx);
bool handle_payload(const watch_notify::UnknownPayload& payload,
C_NotifyAck *ctx);
void process_payload(uint64_t notify_id, uint64_t handle,

View File

@ -23,6 +23,7 @@
#include "librbd/operation/FlattenRequest.h"
#include "librbd/operation/MetadataRemoveRequest.h"
#include "librbd/operation/MetadataSetRequest.h"
#include "librbd/operation/MigrateRequest.h"
#include "librbd/operation/ObjectMapIterate.h"
#include "librbd/operation/RebuildObjectMapRequest.h"
#include "librbd/operation/RenameRequest.h"
@ -249,9 +250,10 @@ struct C_InvokeAsyncRequest : public Context {
CephContext *cct = image_ctx.cct;
ldout(cct, 20) << __func__ << dendl;
Context *ctx = util::create_context_callback<
C_InvokeAsyncRequest<I>, &C_InvokeAsyncRequest<I>::handle_remote_request>(
this);
Context *ctx = util::create_async_context_callback(
image_ctx, util::create_context_callback<
C_InvokeAsyncRequest<I>,
&C_InvokeAsyncRequest<I>::handle_remote_request>(this));
remote(ctx);
}
@ -1536,6 +1538,84 @@ void Operations<I>::execute_metadata_remove(const std::string &key,
request->send();
}
template <typename I>
int Operations<I>::migrate(ProgressContext &prog_ctx) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << "migrate" << dendl;
int r = m_image_ctx.state->refresh_if_required();
if (r < 0) {
return r;
}
if (m_image_ctx.read_only) {
return -EROFS;
}
{
RWLock::RLocker parent_locker(m_image_ctx.parent_lock);
if (m_image_ctx.migration_info.empty()) {
lderr(cct) << "image has no migrating parent" << dendl;
return -EINVAL;
}
}
uint64_t request_id = ++m_async_request_seq;
r = invoke_async_request("migrate", false,
boost::bind(&Operations<I>::execute_migrate, this,
boost::ref(prog_ctx), _1),
boost::bind(&ImageWatcher<I>::notify_migrate,
m_image_ctx.image_watcher, request_id,
boost::ref(prog_ctx), _1));
if (r < 0 && r != -EINVAL) {
return r;
}
ldout(cct, 20) << "migrate finished" << dendl;
return 0;
}
template <typename I>
void Operations<I>::execute_migrate(ProgressContext &prog_ctx,
Context *on_finish) {
assert(m_image_ctx.owner_lock.is_locked());
assert(m_image_ctx.exclusive_lock == nullptr ||
m_image_ctx.exclusive_lock->is_lock_owner());
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << "migrate" << dendl;
if (m_image_ctx.read_only || m_image_ctx.operations_disabled) {
on_finish->complete(-EROFS);
return;
}
m_image_ctx.snap_lock.get_read();
m_image_ctx.parent_lock.get_read();
if (m_image_ctx.migration_info.empty()) {
lderr(cct) << "image has no migrating parent" << dendl;
m_image_ctx.parent_lock.put_read();
m_image_ctx.snap_lock.put_read();
on_finish->complete(-EINVAL);
return;
}
if (m_image_ctx.snap_id != CEPH_NOSNAP) {
lderr(cct) << "snapshots cannot be migrated" << dendl;
m_image_ctx.parent_lock.put_read();
m_image_ctx.snap_lock.put_read();
on_finish->complete(-EROFS);
return;
}
m_image_ctx.parent_lock.put_read();
m_image_ctx.snap_lock.put_read();
operation::MigrateRequest<I> *req = new operation::MigrateRequest<I>(
m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), prog_ctx);
req->send();
}
template <typename I>
int Operations<I>::prepare_image_update(bool request_lock) {
assert(m_image_ctx.owner_lock.is_locked() &&

View File

@ -100,6 +100,9 @@ public:
int metadata_remove(const std::string &key);
void execute_metadata_remove(const std::string &key, Context *on_finish);
int migrate(ProgressContext &prog_ctx);
void execute_migrate(ProgressContext &prog_ctx, Context *on_finish);
int prepare_image_update(bool request_lock);
private:

View File

@ -6,6 +6,7 @@
#include "include/types.h"
#include "cls/rbd/cls_rbd_types.h"
#include "deep_copy/Types.h"
#include <map>
#include <string>
@ -117,6 +118,29 @@ struct SnapInfo {
enum {
OPEN_FLAG_SKIP_OPEN_PARENT = 1 << 0,
OPEN_FLAG_OLD_FORMAT = 1 << 1,
OPEN_FLAG_IGNORE_MIGRATING = 1 << 2,
};
struct MigrationInfo {
int64_t pool_id = -1;
std::string image_name;
std::string image_id;
deep_copy::SnapMap snap_map;
uint64_t overlap = 0;
bool flatten = false;
MigrationInfo() {
}
MigrationInfo(int64_t pool_id, std::string image_name, std::string image_id,
const deep_copy::SnapMap &snap_map, uint64_t overlap,
bool flatten)
: pool_id(pool_id), image_name(image_name), image_id(image_id),
snap_map(snap_map), overlap(overlap), flatten(flatten) {
}
bool empty() const {
return pool_id == -1;
}
};
} // namespace librbd

View File

@ -368,6 +368,9 @@ void NotifyMessage::decode(bufferlist::const_iterator& iter) {
case NOTIFY_OP_UPDATE_FEATURES:
payload = UpdateFeaturesPayload();
break;
case NOTIFY_OP_MIGRATE:
payload = MigratePayload();
break;
default:
payload = UnknownPayload();
break;
@ -402,6 +405,7 @@ void NotifyMessage::generate_test_instances(std::list<NotifyMessage *> &o) {
o.push_back(new NotifyMessage(RebuildObjectMapPayload(AsyncRequestId(ClientId(0, 1), 2))));
o.push_back(new NotifyMessage(RenamePayload("foo")));
o.push_back(new NotifyMessage(UpdateFeaturesPayload(1, true)));
o.push_back(new NotifyMessage(MigratePayload(AsyncRequestId(ClientId(0, 1), 2))));
}
void ResponseMessage::encode(bufferlist& bl) const {
@ -477,6 +481,9 @@ std::ostream &operator<<(std::ostream &out,
case NOTIFY_OP_UPDATE_FEATURES:
out << "UpdateFeatures";
break;
case NOTIFY_OP_MIGRATE:
out << "Migrate";
break;
default:
out << "Unknown (" << static_cast<uint32_t>(op) << ")";
break;

View File

@ -65,6 +65,7 @@ enum NotifyOp {
NOTIFY_OP_SNAP_UNPROTECT = 13,
NOTIFY_OP_RENAME = 14,
NOTIFY_OP_UPDATE_FEATURES = 15,
NOTIFY_OP_MIGRATE = 16,
};
struct AcquiredLockPayload {
@ -301,6 +302,14 @@ struct UpdateFeaturesPayload {
void dump(Formatter *f) const;
};
struct MigratePayload : public AsyncRequestPayloadBase {
static const NotifyOp NOTIFY_OP = NOTIFY_OP_MIGRATE;
static const bool CHECK_FOR_REFRESH = true;
MigratePayload() {}
MigratePayload(const AsyncRequestId &id) : AsyncRequestPayloadBase(id) {}
};
struct UnknownPayload {
static const NotifyOp NOTIFY_OP = static_cast<NotifyOp>(-1);
static const bool CHECK_FOR_REFRESH = false;
@ -326,6 +335,7 @@ typedef boost::variant<AcquiredLockPayload,
RebuildObjectMapPayload,
RenamePayload,
UpdateFeaturesPayload,
MigratePayload,
UnknownPayload> Payload;
struct NotifyMessage {

1336
src/librbd/api/Migration.cc Normal file

File diff suppressed because it is too large Load Diff

100
src/librbd/api/Migration.h Normal file
View File

@ -0,0 +1,100 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#ifndef CEPH_LIBRBD_API_MIGRATION_H
#define CEPH_LIBRBD_API_MIGRATION_H
#include "include/int_types.h"
#include "include/rbd/librbd.hpp"
#include "cls/rbd/cls_rbd_types.h"
#include <vector>
namespace librados {
class IoCtx;
}
namespace librbd {
class ImageCtx;
namespace api {
template <typename ImageCtxT = librbd::ImageCtx>
class Migration {
public:
static int prepare(librados::IoCtx& io_ctx, const std::string &image_name,
librados::IoCtx& dest_io_ctx,
const std::string &dest_image_name, ImageOptions& opts);
static int execute(librados::IoCtx& io_ctx, const std::string &image_name,
ProgressContext &prog_ctx);
static int abort(librados::IoCtx& io_ctx, const std::string &image_name,
ProgressContext &prog_ctx);
static int commit(librados::IoCtx& io_ctx, const std::string &image_name,
ProgressContext &prog_ctx);
static int status(librados::IoCtx& io_ctx, const std::string &image_name,
image_migration_status_t *status);
private:
CephContext* m_cct;
ImageCtxT *m_src_image_ctx;
librados::IoCtx m_src_io_ctx;
librados::IoCtx &m_dst_io_ctx;
bool m_src_old_format;
std::string m_src_image_name;
std::string m_src_image_id;
std::string m_src_header_oid;
std::string m_dst_image_name;
std::string m_dst_image_id;
std::string m_dst_header_oid;
ImageOptions &m_image_options;
bool m_flatten;
bool m_mirroring;
ProgressContext *m_prog_ctx;
cls::rbd::MigrationSpec m_src_migration_spec;
cls::rbd::MigrationSpec m_dst_migration_spec;
Migration(ImageCtxT *image_ctx, librados::IoCtx& dest_io_ctx,
const std::string &dest_image_name, const std::string &dst_image_id,
ImageOptions& opts, bool flatten, bool mirroring,
cls::rbd::MigrationState state, const std::string &state_desc,
ProgressContext *prog_ctx);
int prepare();
int execute();
int abort();
int commit();
int status(image_migration_status_t *status);
int set_state(cls::rbd::MigrationState state, const std::string &description);
int list_snaps(std::vector<librbd::snap_info_t> *snaps = nullptr);
int disable_mirroring(ImageCtxT *image_ctx, bool *was_enabled);
int enable_mirroring(ImageCtxT *image_ctx, bool was_enabled);
int set_migration();
int unlink_src_image();
int relink_src_image();
int create_dst_image();
int remove_group(ImageCtxT *image_ctx, group_info_t *group_info);
int add_group(ImageCtxT *image_ctx, group_info_t &group_info);
int update_group(ImageCtxT *from_image_ctx, ImageCtxT *to_image_ctx);
int remove_migration(ImageCtxT *image_ctx);
int remove_src_image();
int v1_set_migration();
int v2_set_migration();
int v1_unlink_src_image();
int v2_unlink_src_image();
int v1_relink_src_image();
int v2_relink_src_image();
};
} // namespace api
} // namespace librbd
extern template class librbd::api::Migration<librbd::ImageCtx>;
#endif // CEPH_LIBRBD_API_MIGRATION_H

View File

@ -329,6 +329,11 @@ void ObjectCopyRequest<I>::send_write_object() {
librados::ObjectWriteOperation op;
uint64_t buffer_offset;
if (!m_dst_image_ctx->migration_info.empty()) {
cls_client::assert_snapc_seq(&op, dst_snap_seq,
cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ);
}
for (auto &copy_op : copy_ops) {
switch (copy_op.type) {
case COPY_OP_TYPE_WRITE:
@ -366,7 +371,7 @@ void ObjectCopyRequest<I>::send_write_object() {
}
}
if (op.size() == 0) {
if (op.size() == (m_dst_image_ctx->migration_info.empty() ? 0 : 1)) {
handle_write_object(0);
return;
}
@ -388,7 +393,7 @@ void ObjectCopyRequest<I>::send_write_object() {
});
librados::AioCompletion *comp = create_rados_callback(ctx);
int r = m_dst_io_ctx.aio_operate(m_dst_oid, comp, &op, dst_snap_seq,
dst_snap_ids);
dst_snap_ids, nullptr);
assert(r == 0);
comp->release();
}
@ -399,6 +404,9 @@ void ObjectCopyRequest<I>::handle_write_object(int r) {
if (r == -ENOENT) {
r = 0;
} else if (r == -ERANGE) {
ldout(m_cct, 10) << "concurrent deep copy" << dendl;
r = 0;
}
if (r < 0) {
lderr(m_cct) << "failed to write to destination object: " << cpp_strerror(r)

View File

@ -5,6 +5,7 @@
#define CEPH_LIBRBD_DEEP_COPY_TYPES_H
#include "include/int_types.h"
#include "include/rados/librados.hpp"
#include <boost/optional.hpp>
namespace librbd {

View File

@ -238,7 +238,12 @@ void CloneRequest<I>::send_open() {
using klass = CloneRequest<I>;
Context *ctx = create_context_callback<klass, &klass::handle_open>(this);
m_imctx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT, ctx);
uint64_t flags = OPEN_FLAG_SKIP_OPEN_PARENT;
if ((m_features & RBD_FEATURE_MIGRATING) != 0) {
flags |= OPEN_FLAG_IGNORE_MIGRATING;
}
m_imctx->state->open(flags, ctx);
}
template <typename I>

View File

@ -290,6 +290,7 @@ void CloseRequest<I>::handle_close_parent(int r) {
ldout(cct, 10) << this << " " << __func__ << ": r=" << r << dendl;
delete m_image_ctx->parent;
m_image_ctx->parent = nullptr;
save_result(r);
if (r < 0) {
lderr(cct) << "error closing parent image: " << cpp_strerror(r) << dendl;

View File

@ -33,6 +33,9 @@ OpenRequest<I>::OpenRequest(I *image_ctx, uint64_t flags,
if ((flags & OPEN_FLAG_OLD_FORMAT) != 0) {
m_image_ctx->old_format = true;
}
if ((flags & OPEN_FLAG_IGNORE_MIGRATING) != 0) {
m_image_ctx->ignore_migrating = true;
}
}
template <typename I>

View File

@ -24,43 +24,55 @@ using util::create_async_context_callback;
using util::create_context_callback;
template <typename I>
RefreshParentRequest<I>::RefreshParentRequest(I &child_image_ctx,
const ParentInfo &parent_md,
Context *on_finish)
RefreshParentRequest<I>::RefreshParentRequest(
I &child_image_ctx, const ParentInfo &parent_md,
const MigrationInfo &migration_info, Context *on_finish)
: m_child_image_ctx(child_image_ctx), m_parent_md(parent_md),
m_on_finish(on_finish), m_parent_image_ctx(nullptr),
m_parent_snap_id(CEPH_NOSNAP), m_error_result(0) {
m_migration_info(migration_info), m_on_finish(on_finish),
m_parent_image_ctx(nullptr), m_parent_snap_id(CEPH_NOSNAP),
m_error_result(0) {
}
template <typename I>
bool RefreshParentRequest<I>::is_refresh_required(I &child_image_ctx,
const ParentInfo &parent_md) {
bool RefreshParentRequest<I>::is_refresh_required(
I &child_image_ctx, const ParentInfo &parent_md,
const MigrationInfo &migration_info) {
assert(child_image_ctx.snap_lock.is_locked());
assert(child_image_ctx.parent_lock.is_locked());
return (is_open_required(child_image_ctx, parent_md) ||
is_close_required(child_image_ctx, parent_md));
return (is_open_required(child_image_ctx, parent_md, migration_info) ||
is_close_required(child_image_ctx, parent_md, migration_info));
}
template <typename I>
bool RefreshParentRequest<I>::is_close_required(I &child_image_ctx,
const ParentInfo &parent_md) {
bool RefreshParentRequest<I>::is_close_required(
I &child_image_ctx, const ParentInfo &parent_md,
const MigrationInfo &migration_info) {
return (child_image_ctx.parent != nullptr &&
(parent_md.spec.pool_id == -1 || parent_md.overlap == 0));
!does_parent_exist(child_image_ctx, parent_md, migration_info));
}
template <typename I>
bool RefreshParentRequest<I>::is_open_required(I &child_image_ctx,
const ParentInfo &parent_md) {
return (parent_md.spec.pool_id > -1 && parent_md.overlap > 0 &&
bool RefreshParentRequest<I>::is_open_required(
I &child_image_ctx, const ParentInfo &parent_md,
const MigrationInfo &migration_info) {
return (does_parent_exist(child_image_ctx, parent_md, migration_info) &&
(child_image_ctx.parent == nullptr ||
child_image_ctx.parent->md_ctx.get_id() != parent_md.spec.pool_id ||
child_image_ctx.parent->id != parent_md.spec.image_id ||
child_image_ctx.parent->snap_id != parent_md.spec.snap_id));
}
template <typename I>
bool RefreshParentRequest<I>::does_parent_exist(
I &child_image_ctx, const ParentInfo &parent_md,
const MigrationInfo &migration_info) {
return (parent_md.spec.pool_id > -1 && parent_md.overlap > 0) ||
!migration_info.empty();
}
template <typename I>
void RefreshParentRequest<I>::send() {
if (is_open_required(m_child_image_ctx, m_parent_md)) {
if (is_open_required(m_child_image_ctx, m_parent_md, m_migration_info)) {
send_open_parent();
} else {
// parent will be closed (if necessary) during finalize
@ -108,10 +120,15 @@ void RefreshParentRequest<I>::send_open_parent() {
// TODO support clone v2 parent namespaces
parent_io_ctx.set_namespace(m_child_image_ctx.md_ctx.get_namespace());
// since we don't know the image and snapshot name, set their ids and
// reset the snap_name and snap_exists fields after we read the header
m_parent_image_ctx = new I("", m_parent_md.spec.image_id, NULL, parent_io_ctx,
true);
std::string image_name;
uint64_t flags = 0;
if (!m_migration_info.empty() && !m_migration_info.image_name.empty()) {
image_name = m_migration_info.image_name;
flags |= OPEN_FLAG_OLD_FORMAT;
}
m_parent_image_ctx = new I(image_name, m_parent_md.spec.image_id, nullptr,
parent_io_ctx, true);
m_parent_image_ctx->child = &m_child_image_ctx;
// set rados flags for reading the parent image
@ -121,10 +138,6 @@ void RefreshParentRequest<I>::send_open_parent() {
m_parent_image_ctx->set_read_flag(librados::OPERATION_LOCALIZE_READS);
}
uint64_t flags = 0;
if (m_parent_md.spec.image_id.empty()) {
flags |= OPEN_FLAG_OLD_FORMAT;
}
using klass = RefreshParentRequest<I>;
Context *ctx = create_async_context_callback(
m_child_image_ctx, create_context_callback<
@ -150,6 +163,10 @@ Context *RefreshParentRequest<I>::handle_open_parent(int *result) {
return m_on_finish;
}
if (m_parent_md.spec.snap_id == CEPH_NOSNAP) {
return m_on_finish;
}
send_set_parent_snap();
return nullptr;
}

View File

@ -20,12 +20,15 @@ class RefreshParentRequest {
public:
static RefreshParentRequest *create(ImageCtxT &child_image_ctx,
const ParentInfo &parent_md,
const MigrationInfo &migration_info,
Context *on_finish) {
return new RefreshParentRequest(child_image_ctx, parent_md, on_finish);
return new RefreshParentRequest(child_image_ctx, parent_md, migration_info,
on_finish);
}
static bool is_refresh_required(ImageCtxT &child_image_ctx,
const ParentInfo &parent_md);
const ParentInfo &parent_md,
const MigrationInfo &migration_info);
void send();
void apply();
@ -59,10 +62,11 @@ private:
*/
RefreshParentRequest(ImageCtxT &child_image_ctx, const ParentInfo &parent_md,
Context *on_finish);
const MigrationInfo &migration_info, Context *on_finish);
ImageCtxT &m_child_image_ctx;
ParentInfo m_parent_md;
MigrationInfo m_migration_info;
Context *m_on_finish;
ImageCtxT *m_parent_image_ctx;
@ -71,9 +75,14 @@ private:
int m_error_result;
static bool is_close_required(ImageCtxT &child_image_ctx,
const ParentInfo &parent_md);
const ParentInfo &parent_md,
const MigrationInfo &migration_info);
static bool is_open_required(ImageCtxT &child_image_ctx,
const ParentInfo &parent_md);
const ParentInfo &parent_md,
const MigrationInfo &migration_info);
static bool does_parent_exist(ImageCtxT &child_image_ctx,
const ParentInfo &parent_md,
const MigrationInfo &migration_info);
void send_open_parent();
Context *handle_open_parent(int *result);

View File

@ -15,6 +15,7 @@
#include "librbd/Journal.h"
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
#include "librbd/deep_copy/Utils.h"
#include "librbd/image/RefreshParentRequest.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ImageDispatchSpec.h"
@ -67,6 +68,90 @@ void RefreshRequest<I>::send() {
}
}
template <typename I>
void RefreshRequest<I>::send_get_migration_header() {
if (m_image_ctx.ignore_migrating) {
if (m_image_ctx.old_format) {
send_v1_get_snapshots();
} else {
send_v2_get_metadata();
}
return;
}
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << this << " " << __func__ << dendl;
librados::ObjectReadOperation op;
cls_client::migration_get_start(&op);
using klass = RefreshRequest<I>;
librados::AioCompletion *comp =
create_rados_callback<klass, &klass::handle_get_migration_header>(this);
m_out_bl.clear();
m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
&m_out_bl);
comp->release();
}
template <typename I>
Context *RefreshRequest<I>::handle_get_migration_header(int *result) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
if (*result == 0) {
auto it = m_out_bl.cbegin();
*result = cls_client::migration_get_finish(&it, &m_migration_spec);
} else if (*result == -ENOENT) {
ldout(cct, 5) << this << " " << __func__ << ": no migration header found"
<< ", retrying" << dendl;
send();
return nullptr;
}
if (*result < 0) {
lderr(cct) << "failed to retrieve migration header: "
<< cpp_strerror(*result) << dendl;
return m_on_finish;
}
switch(m_migration_spec.header_type) {
case cls::rbd::MIGRATION_HEADER_TYPE_SRC:
if (!m_image_ctx.read_only) {
lderr(cct) << "image being migrated" << dendl;
*result = -EROFS;
return m_on_finish;
}
ldout(cct, 1) << this << " " << __func__ << ": migrating to: "
<< m_migration_spec << dendl;
break;
case cls::rbd::MIGRATION_HEADER_TYPE_DST:
ldout(cct, 1) << this << " " << __func__ << ": migrating from: "
<< m_migration_spec << dendl;
if (m_migration_spec.state != cls::rbd::MIGRATION_STATE_PREPARED &&
m_migration_spec.state != cls::rbd::MIGRATION_STATE_EXECUTING &&
m_migration_spec.state != cls::rbd::MIGRATION_STATE_EXECUTED) {
ldout(cct, 5) << this << " " << __func__ << ": current migration state: "
<< m_migration_spec.state << ", retrying" << dendl;
send();
return nullptr;
}
break;
default:
ldout(cct, 1) << this << " " << __func__ << ": migration type "
<< m_migration_spec.header_type << dendl;
*result = -EBADMSG;
return m_on_finish;
}
if (m_image_ctx.old_format) {
send_v1_get_snapshots();
} else {
send_v2_get_metadata();
}
return nullptr;
}
template <typename I>
void RefreshRequest<I>::send_v1_read_header() {
CephContext *cct = m_image_ctx.cct;
@ -91,6 +176,7 @@ Context *RefreshRequest<I>::handle_v1_read_header(int *result) {
ldout(cct, 10) << this << " " << __func__ << ": " << "r=" << *result << dendl;
rbd_obj_header_ondisk v1_header;
bool migrating = false;
if (*result < 0) {
return m_on_finish;
} else if (m_out_bl.length() < sizeof(v1_header)) {
@ -99,16 +185,27 @@ Context *RefreshRequest<I>::handle_v1_read_header(int *result) {
return m_on_finish;
} else if (memcmp(RBD_HEADER_TEXT, m_out_bl.c_str(),
sizeof(RBD_HEADER_TEXT)) != 0) {
lderr(cct) << "unrecognized v1 header" << dendl;
*result = -ENXIO;
return m_on_finish;
if (memcmp(RBD_MIGRATE_HEADER_TEXT, m_out_bl.c_str(),
sizeof(RBD_MIGRATE_HEADER_TEXT)) == 0) {
ldout(cct, 1) << this << " " << __func__ << ": migration v1 header detected"
<< dendl;
migrating = true;
} else {
lderr(cct) << "unrecognized v1 header" << dendl;
*result = -ENXIO;
return m_on_finish;
}
}
memcpy(&v1_header, m_out_bl.c_str(), sizeof(v1_header));
m_order = v1_header.options.order;
m_size = v1_header.image_size;
m_object_prefix = v1_header.block_name;
send_v1_get_snapshots();
if (migrating) {
send_get_migration_header();
} else {
send_v1_get_snapshots();
}
return nullptr;
}
@ -299,6 +396,12 @@ Context *RefreshRequest<I>::handle_v2_get_mutable_metadata(int *result) {
m_incomplete_update = true;
}
if ((m_features & RBD_FEATURE_MIGRATING) != 0) {
ldout(cct, 1) << "migrating feature set" << dendl;
send_get_migration_header();
return nullptr;
}
send_v2_get_metadata();
return nullptr;
}
@ -668,9 +771,11 @@ void RefreshRequest<I>::send_v2_refresh_parent() {
RWLock::RLocker parent_locker(m_image_ctx.parent_lock);
ParentInfo parent_md;
int r = get_parent_info(m_image_ctx.snap_id, &parent_md);
MigrationInfo migration_info;
int r = get_parent_info(m_image_ctx.snap_id, &parent_md, &migration_info);
if (!m_skip_open_parent_image && (r < 0 ||
RefreshParentRequest<I>::is_refresh_required(m_image_ctx, parent_md))) {
RefreshParentRequest<I>::is_refresh_required(m_image_ctx, parent_md,
migration_info))) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << this << " " << __func__ << dendl;
@ -678,7 +783,7 @@ void RefreshRequest<I>::send_v2_refresh_parent() {
Context *ctx = create_context_callback<
klass, &klass::handle_v2_refresh_parent>(this);
m_refresh_parent = RefreshParentRequest<I>::create(
m_image_ctx, parent_md, ctx);
m_image_ctx, parent_md, migration_info, ctx);
}
}
@ -1140,6 +1245,8 @@ void RefreshRequest<I>::apply() {
m_image_ctx.lock_tag = m_lock_tag;
m_image_ctx.exclusive_locked = m_exclusive_locked;
std::map<uint64_t, uint64_t> migration_reverse_snap_seq;
if (m_image_ctx.old_format) {
m_image_ctx.order = m_order;
m_image_ctx.features = 0;
@ -1155,7 +1262,15 @@ void RefreshRequest<I>::apply() {
m_image_ctx.operations_disabled = (
(m_op_features & ~RBD_OPERATION_FEATURES_ALL) != 0ULL);
m_image_ctx.group_spec = m_group_spec;
m_image_ctx.parent_md = m_parent_md;
if (get_migration_info(&m_image_ctx.parent_md,
&m_image_ctx.migration_info)) {
for (auto it : m_image_ctx.migration_info.snap_map) {
migration_reverse_snap_seq[it.second.front()] = it.first;
}
} else {
m_image_ctx.parent_md = m_parent_md;
m_image_ctx.migration_info = {};
}
}
for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
@ -1174,6 +1289,7 @@ void RefreshRequest<I>::apply() {
m_image_ctx.snaps.clear();
m_image_ctx.snap_info.clear();
m_image_ctx.snap_ids.clear();
auto overlap = m_image_ctx.parent_md.overlap;
for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
uint64_t flags = m_image_ctx.old_format ? 0 : m_snap_flags[i];
uint8_t protection_status = m_image_ctx.old_format ?
@ -1181,15 +1297,27 @@ void RefreshRequest<I>::apply() {
m_snap_protection[i];
ParentInfo parent;
if (!m_image_ctx.old_format) {
parent = m_snap_parents[i];
if (!m_image_ctx.migration_info.empty()) {
parent = m_image_ctx.parent_md;
auto it = migration_reverse_snap_seq.find(m_snapc.snaps[i].val);
if (it != migration_reverse_snap_seq.end()) {
parent.spec.snap_id = it->second;
parent.overlap = m_snap_infos[i].image_size;
} else {
overlap = std::min(overlap, m_snap_infos[i].image_size);
parent.overlap = overlap;
}
} else {
parent = m_snap_parents[i];
}
}
m_image_ctx.add_snap(m_snap_infos[i].snapshot_namespace,
m_snap_infos[i].name, m_snapc.snaps[i].val,
m_snap_infos[i].image_size, parent,
protection_status, flags,
m_snap_infos[i].timestamp);
}
m_image_ctx.parent_md.overlap = std::min(overlap, m_image_ctx.size);
m_image_ctx.snapc = m_snapc;
if (m_image_ctx.snap_id != CEPH_NOSNAP &&
@ -1240,14 +1368,19 @@ void RefreshRequest<I>::apply() {
template <typename I>
int RefreshRequest<I>::get_parent_info(uint64_t snap_id,
ParentInfo *parent_md) {
if (snap_id == CEPH_NOSNAP) {
ParentInfo *parent_md,
MigrationInfo *migration_info) {
if (get_migration_info(parent_md, migration_info)) {
return 0;
} else if (snap_id == CEPH_NOSNAP) {
*parent_md = m_parent_md;
*migration_info = {};
return 0;
} else {
for (size_t i = 0; i < m_snapc.snaps.size(); ++i) {
if (m_snapc.snaps[i].val == snap_id) {
*parent_md = m_snap_parents[i];
*migration_info = {};
return 0;
}
}
@ -1255,6 +1388,46 @@ int RefreshRequest<I>::get_parent_info(uint64_t snap_id,
return -ENOENT;
}
template <typename I>
bool RefreshRequest<I>::get_migration_info(ParentInfo *parent_md,
MigrationInfo *migration_info) {
if (m_migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_DST ||
(m_migration_spec.state != cls::rbd::MIGRATION_STATE_PREPARED &&
m_migration_spec.state != cls::rbd::MIGRATION_STATE_EXECUTING)) {
assert(m_migration_spec.header_type == cls::rbd::MIGRATION_HEADER_TYPE_SRC ||
m_migration_spec.pool_id == -1 ||
m_migration_spec.state == cls::rbd::MIGRATION_STATE_EXECUTED);
return false;
}
parent_md->spec.pool_id = m_migration_spec.pool_id;
parent_md->spec.image_id = m_migration_spec.image_id;
parent_md->spec.snap_id = CEPH_NOSNAP;
parent_md->overlap = m_migration_spec.overlap;
*migration_info = {m_migration_spec.pool_id, m_migration_spec.image_name,
m_migration_spec.image_id, {}, m_migration_spec.overlap,
m_migration_spec.flatten};
auto snap_seqs = m_migration_spec.snap_seqs;
// If new snapshots have been created on destination image after
// migration stared, map the source CEPH_NOSNAP to the earliest of
// these snapshots.
snapid_t snap_id = snap_seqs.empty() ? 0 : snap_seqs.rbegin()->second;
auto it = std::upper_bound(m_snapc.snaps.rbegin(), m_snapc.snaps.rend(),
snap_id);
if (it != m_snapc.snaps.rend()) {
snap_seqs[CEPH_NOSNAP] = *it;
} else {
snap_seqs[CEPH_NOSNAP] = CEPH_NOSNAP;
}
deep_copy::util::compute_snap_map(0, CEPH_NOSNAP, snap_seqs,
&migration_info->snap_map);
return true;
}
} // namespace image
} // namespace librbd

View File

@ -43,16 +43,19 @@ private:
/**
* @verbatim
*
* <start>
* |
* | (v1)
* |-----> V1_READ_HEADER ---> V1_GET_SNAPSHOTS ---> V1_GET_LOCKS
* | |
* | (v2) v
* \-----> V2_GET_MUTABLE_METADATA <apply>
* | |
* v |
* V2_GET_METADATA |
* <start> < * * * * * * * * * * * * * * * * * * * * * * * * * * (ENOENT)
* ^ | *
* * | (v1) *
* * |-----> V1_READ_HEADER -------------> GET_MIGRATION_HEADER (skip if not
* * | | migrating)
* * | (v2) v
* * \-----> V2_GET_MUTABLE_METADATA V1_GET_SNAPSHOTS
* * | |
* * v v
* * * * * * GET_MIGRATION_HEADER (skip if not V1_GET_LOCKS
* (ENOENT) | migrating) |
* v v
* V2_GET_METADATA <apply>
* | |
* v |
* V2_GET_FLAGS |
@ -119,6 +122,7 @@ private:
bool m_skip_open_parent_image;
Context *m_on_finish;
cls::rbd::MigrationSpec m_migration_spec;
int m_error_result;
bool m_flush_aio;
decltype(m_image_ctx.exclusive_lock) m_exclusive_lock;
@ -156,6 +160,9 @@ private:
bool m_blocked_writes = false;
bool m_incomplete_update = false;
void send_get_migration_header();
Context *handle_get_migration_header(int *result);
void send_v1_read_header();
Context *handle_v1_read_header(int *result);
@ -234,7 +241,9 @@ private:
}
void apply();
int get_parent_info(uint64_t snap_id, ParentInfo *parent_md);
int get_parent_info(uint64_t snap_id, ParentInfo *parent_md,
MigrationInfo *migration_info);
bool get_migration_info(ParentInfo *parent_md, MigrationInfo *migration_info);
};
} // namespace image

View File

@ -202,6 +202,13 @@ template<typename I>
void RemoveRequest<I>::validate_image_removal() {
ldout(m_cct, 20) << dendl;
if (!m_image_ctx->ignore_migrating &&
m_image_ctx->test_features(RBD_FEATURE_MIGRATING)) {
lderr(m_cct) << "image in migration state - not removing" << dendl;
send_close_image(-EBUSY);
return;
}
check_image_snaps();
}

View File

@ -190,8 +190,8 @@ Context *SetSnapRequest<I>::send_refresh_parent(int *result) {
}
parent_md = *parent_info;
refresh_parent = RefreshParentRequest<I>::is_refresh_required(m_image_ctx,
parent_md);
refresh_parent = RefreshParentRequest<I>::is_refresh_required(
m_image_ctx, parent_md, m_image_ctx.migration_info);
}
if (!refresh_parent) {
@ -212,6 +212,7 @@ Context *SetSnapRequest<I>::send_refresh_parent(int *result) {
Context *ctx = create_context_callback<
klass, &klass::handle_refresh_parent>(this);
m_refresh_parent = RefreshParentRequest<I>::create(m_image_ctx, parent_md,
m_image_ctx.migration_info,
ctx);
m_refresh_parent->send();
return nullptr;

View File

@ -244,8 +244,14 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2)
off += r;
} while (r == READ_SIZE);
static_assert(sizeof(RBD_HEADER_TEXT) == sizeof(RBD_MIGRATE_HEADER_TEXT),
"length of rbd headers must be the same");
if (header.length() < sizeof(RBD_HEADER_TEXT) ||
memcmp(RBD_HEADER_TEXT, header.c_str(), sizeof(RBD_HEADER_TEXT))) {
(memcmp(RBD_HEADER_TEXT, header.c_str(),
sizeof(RBD_HEADER_TEXT)) != 0 &&
memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(),
sizeof(RBD_MIGRATE_HEADER_TEXT)) != 0)) {
CephContext *cct = (CephContext *)io_ctx.cct();
lderr(cct) << "unrecognized header format" << dendl;
return -ENXIO;
@ -1419,6 +1425,12 @@ bool compare_by_name(const child_info_t& c1, const child_info_t& c2)
}
ictx->owner_lock.put_read();
if (!ictx->migration_info.empty()) {
lderr(cct) << "cannot move migrating image to trash" << dendl;
ictx->state->close();
return -EINVAL;
}
utime_t delete_time{ceph_clock_now()};
utime_t deferment_end_time{delete_time};
deferment_end_time += delay;

View File

@ -12,6 +12,7 @@
#include "librbd/ImageCtx.h"
#include "librbd/ObjectMap.h"
#include "librbd/Utils.h"
#include "librbd/deep_copy/ObjectCopyRequest.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ImageRequest.h"
#include "librbd/io/ObjectRequest.h"
@ -203,10 +204,48 @@ bool CopyupRequest<I>::is_copyup_required() {
return false;
}
template <typename I>
bool CopyupRequest<I>::is_update_object_map_required() {
RWLock::RLocker owner_locker(m_ictx->owner_lock);
RWLock::RLocker snap_locker(m_ictx->snap_lock);
if (m_ictx->object_map == nullptr) {
return false;
}
if (!is_deep_copy()) {
return false;
}
auto it = m_ictx->migration_info.snap_map.find(CEPH_NOSNAP);
assert(it != m_ictx->migration_info.snap_map.end());
return it->second[0] != CEPH_NOSNAP;
}
template <typename I>
bool CopyupRequest<I>::is_deep_copy() const {
return !m_ictx->migration_info.empty() &&
m_ictx->migration_info.snap_map.size() > 1;
}
template <typename I>
void CopyupRequest<I>::send()
{
m_state = STATE_READ_FROM_PARENT;
if (is_deep_copy()) {
bool flatten = is_copyup_required() ? true : m_ictx->migration_info.flatten;
auto req = deep_copy::ObjectCopyRequest<I>::create(
m_ictx->parent, m_ictx->parent->parent /* TODO */, m_ictx,
m_ictx->migration_info.snap_map, m_object_no, flatten,
util::create_context_callback(this));
ldout(m_ictx->cct, 20) << "deep copy object req " << req
<< ", object_no " << m_object_no
<< ", flatten " << flatten
<< dendl;
req->send();
return;
}
AioCompletion *comp = AioCompletion::create_and_start(
this, m_ictx, AIO_TYPE_READ);
@ -240,8 +279,8 @@ bool CopyupRequest<I>::should_complete(int r)
ldout(cct, 20) << "READ_FROM_PARENT" << dendl;
remove_from_list();
if (r >= 0 || r == -ENOENT) {
if (!is_copyup_required()) {
ldout(cct, 20) << "nop, skipping" << dendl;
if (!is_copyup_required() && !is_update_object_map_required()) {
ldout(cct, 20) << "skipping" << dendl;
return true;
}
@ -257,6 +296,10 @@ bool CopyupRequest<I>::should_complete(int r)
case STATE_OBJECT_MAP:
ldout(cct, 20) << "OBJECT_MAP" << dendl;
assert(r == 0);
if (!is_copyup_required()) {
ldout(cct, 20) << "skipping copyup" << dendl;
return true;
}
return send_copyup();
case STATE_COPYUP:
@ -310,9 +353,25 @@ bool CopyupRequest<I>::send_object_map_head() {
assert(m_ictx->exclusive_lock->is_lock_owner());
RWLock::WLocker object_map_locker(m_ictx->object_map_lock);
if (!m_ictx->snaps.empty()) {
m_snap_ids.insert(m_snap_ids.end(), m_ictx->snaps.begin(),
m_ictx->snaps.end());
if (is_deep_copy()) {
// don't copy ids for the snaps updated by object deep copy
std::set<uint64_t> deep_copied;
for (auto &it : m_ictx->migration_info.snap_map) {
if (it.first != CEPH_NOSNAP) {
deep_copied.insert(it.second.front());
}
}
std::copy_if(m_ictx->snaps.begin(), m_ictx->snaps.end(),
std::back_inserter(m_snap_ids),
[&deep_copied](uint64_t i) {
return !deep_copied.count(i);
});
} else {
m_snap_ids.insert(m_snap_ids.end(), m_ictx->snaps.begin(),
m_ictx->snaps.end());
}
}
if (copy_on_read &&
(*m_ictx->object_map)[m_object_no] != OBJECT_EXISTS) {

View File

@ -113,6 +113,8 @@ private:
bool send_object_map();
bool send_copyup();
bool is_copyup_required();
bool is_update_object_map_required();
bool is_deep_copy() const;
};
} // namespace io

View File

@ -478,7 +478,12 @@ void AbstractObjectWriteRequest<I>::write_object() {
librados::ObjectWriteOperation write;
if (m_copyup_enabled) {
ldout(image_ctx->cct, 20) << "guarding write" << dendl;
write.assert_exists();
if (!image_ctx->migration_info.empty()) {
cls_client::assert_snapc_seq(
&write, m_snap_seq, cls::rbd::ASSERT_SNAPC_SEQ_NOT_GT_SNAPSET_SEQ);
} else {
write.assert_exists();
}
}
add_write_hint(&write);
@ -501,7 +506,7 @@ void AbstractObjectWriteRequest<I>::handle_write_object(int r) {
ldout(image_ctx->cct, 20) << "r=" << r << dendl;
r = filter_write_result(r);
if (r == -ENOENT) {
if (r == -ENOENT || (r == -ERANGE && !image_ctx->migration_info.empty())) {
if (m_copyup_enabled) {
copyup();
return;

View File

@ -29,6 +29,7 @@
#include "librbd/api/DiffIterate.h"
#include "librbd/api/Group.h"
#include "librbd/api/Image.h"
#include "librbd/api/Migration.h"
#include "librbd/api/Mirror.h"
#include "librbd/api/Namespace.h"
#include "librbd/api/Snapshot.h"
@ -652,6 +653,105 @@ namespace librbd {
return r;
}
int RBD::migration_prepare(IoCtx& io_ctx, const char *image_name,
IoCtx& dest_io_ctx, const char *dest_image_name,
ImageOptions& opts)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_prepare_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name, dest_io_ctx.get_pool_name().c_str(),
dest_io_ctx.get_id(), dest_image_name, opts.opts);
int r = librbd::api::Migration<>::prepare(io_ctx, image_name, dest_io_ctx,
dest_image_name, opts);
tracepoint(librbd, migration_prepare_exit, r);
return r;
}
int RBD::migration_execute(IoCtx& io_ctx, const char *image_name)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_execute_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
librbd::NoOpProgressContext prog_ctx;
int r = librbd::api::Migration<>::execute(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_execute_exit, r);
return r;
}
int RBD::migration_execute_with_progress(IoCtx& io_ctx,
const char *image_name,
librbd::ProgressContext &prog_ctx)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_execute_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
int r = librbd::api::Migration<>::execute(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_execute_exit, r);
return r;
}
int RBD::migration_abort(IoCtx& io_ctx, const char *image_name)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_abort_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
librbd::NoOpProgressContext prog_ctx;
int r = librbd::api::Migration<>::abort(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_abort_exit, r);
return r;
}
int RBD::migration_abort_with_progress(IoCtx& io_ctx, const char *image_name,
librbd::ProgressContext &prog_ctx)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_abort_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
int r = librbd::api::Migration<>::abort(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_abort_exit, r);
return r;
}
int RBD::migration_commit(IoCtx& io_ctx, const char *image_name)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_commit_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
librbd::NoOpProgressContext prog_ctx;
int r = librbd::api::Migration<>::commit(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_commit_exit, r);
return r;
}
int RBD::migration_commit_with_progress(IoCtx& io_ctx, const char *image_name,
librbd::ProgressContext &prog_ctx)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_commit_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
int r = librbd::api::Migration<>::commit(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_commit_exit, r);
return r;
}
int RBD::migration_status(IoCtx& io_ctx, const char *image_name,
image_migration_status_t *status,
size_t status_size)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_status_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
if (status_size != sizeof(image_migration_status_t)) {
tracepoint(librbd, migration_status_exit, -ERANGE);
return -ERANGE;
}
int r = librbd::api::Migration<>::status(io_ctx, image_name, status);
tracepoint(librbd, migration_status_exit, r);
return r;
}
int RBD::mirror_mode_get(IoCtx& io_ctx, rbd_mirror_mode_t *mirror_mode) {
return librbd::api::Mirror<>::mode_get(io_ctx, mirror_mode);
}
@ -2943,6 +3043,153 @@ extern "C" int rbd_rename(rados_ioctx_t src_p, const char *srcname,
return r;
}
extern "C" int rbd_migration_prepare(rados_ioctx_t p, const char *image_name,
rados_ioctx_t dest_p,
const char *dest_image_name,
rbd_image_options_t opts_)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
librados::IoCtx dest_io_ctx;
librados::IoCtx::from_rados_ioctx_t(dest_p, dest_io_ctx);
tracepoint(librbd, migration_prepare_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name, dest_io_ctx.get_pool_name().c_str(),
dest_io_ctx.get_id(), dest_image_name, opts_);
librbd::ImageOptions opts(opts_);
int r = librbd::api::Migration<>::prepare(io_ctx, image_name, dest_io_ctx,
dest_image_name, opts);
tracepoint(librbd, migration_prepare_exit, r);
return r;
}
extern "C" int rbd_migration_execute(rados_ioctx_t p, const char *image_name)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_execute_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
librbd::NoOpProgressContext prog_ctx;
int r = librbd::api::Migration<>::execute(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_execute_exit, r);
return r;
}
extern "C" int rbd_migration_execute_with_progress(rados_ioctx_t p,
const char *name,
librbd_progress_fn_t fn,
void *data)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_execute_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), name);
librbd::CProgressContext prog_ctx(fn, data);
int r = librbd::api::Migration<>::execute(io_ctx, name, prog_ctx);
tracepoint(librbd, migration_execute_exit, r);
return r;
}
extern "C" int rbd_migration_abort(rados_ioctx_t p, const char *image_name)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_abort_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
librbd::NoOpProgressContext prog_ctx;
int r = librbd::api::Migration<>::abort(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_abort_exit, r);
return r;
}
extern "C" int rbd_migration_abort_with_progress(rados_ioctx_t p,
const char *name,
librbd_progress_fn_t fn,
void *data)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_abort_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), name);
librbd::CProgressContext prog_ctx(fn, data);
int r = librbd::api::Migration<>::abort(io_ctx, name, prog_ctx);
tracepoint(librbd, migration_abort_exit, r);
return r;
}
extern "C" int rbd_migration_commit(rados_ioctx_t p, const char *image_name)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_commit_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
librbd::NoOpProgressContext prog_ctx;
int r = librbd::api::Migration<>::commit(io_ctx, image_name, prog_ctx);
tracepoint(librbd, migration_commit_exit, r);
return r;
}
extern "C" int rbd_migration_commit_with_progress(rados_ioctx_t p,
const char *name,
librbd_progress_fn_t fn,
void *data)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_commit_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), name);
librbd::CProgressContext prog_ctx(fn, data);
int r = librbd::api::Migration<>::commit(io_ctx, name, prog_ctx);
tracepoint(librbd, migration_commit_exit, r);
return r;
}
extern "C" int rbd_migration_status(rados_ioctx_t p, const char *image_name,
rbd_image_migration_status_t *status,
size_t status_size)
{
librados::IoCtx io_ctx;
librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
tracepoint(librbd, migration_status_enter, io_ctx.get_pool_name().c_str(),
io_ctx.get_id(), image_name);
if (status_size != sizeof(rbd_image_migration_status_t)) {
tracepoint(librbd, migration_status_exit, -ERANGE);
return -ERANGE;
}
librbd::image_migration_status_t cpp_status;
int r = librbd::api::Migration<>::status(io_ctx, image_name, &cpp_status);
if (r >= 0) {
status->source_pool_id = cpp_status.source_pool_id;
status->source_image_name = strdup(cpp_status.source_image_name.c_str());
status->source_image_id = strdup(cpp_status.source_image_id.c_str());
status->dest_pool_id = cpp_status.dest_pool_id;
status->dest_image_name = strdup(cpp_status.dest_image_name.c_str());
status->dest_image_id = strdup(cpp_status.dest_image_id.c_str());
status->state = cpp_status.state;
status->state_description = strdup(cpp_status.state_description.c_str());
}
tracepoint(librbd, migration_status_exit, r);
return r;
}
extern "C" void rbd_migration_status_cleanup(rbd_image_migration_status_t *s)
{
free(s->source_image_name);
free(s->source_image_id);
free(s->dest_image_name);
free(s->dest_image_id);
free(s->state_description);
}
extern "C" int rbd_open(rados_ioctx_t p, const char *name, rbd_image_t *image,
const char *snap_name)
{

View File

@ -0,0 +1,226 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include "librbd/operation/MigrateRequest.h"
#include "common/dout.h"
#include "common/errno.h"
#include "librbd/AsyncObjectThrottle.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageCtx.h"
#include "librbd/Utils.h"
#include "librbd/deep_copy/ObjectCopyRequest.h"
#include "librbd/io/AsyncOperation.h"
#include "librbd/io/ImageRequestWQ.h"
#include "librbd/io/ObjectRequest.h"
#include "osdc/Striper.h"
#include <boost/lambda/bind.hpp>
#include <boost/lambda/construct.hpp>
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
#define dout_prefix *_dout << "librbd::MigrateRequest: " << this << " " \
<< __func__ << ": "
namespace librbd {
namespace operation {
using util::create_context_callback;
using util::create_async_context_callback;
namespace {
template <typename I>
class C_MigrateObject : public C_AsyncObjectThrottle<I> {
public:
C_MigrateObject(AsyncObjectThrottle<I> &throttle, I *image_ctx,
::SnapContext snapc, uint64_t object_no)
: C_AsyncObjectThrottle<I>(throttle, *image_ctx), m_snapc(snapc),
m_object_no(object_no) {
}
int send() override {
I &image_ctx = this->m_image_ctx;
assert(image_ctx.owner_lock.is_locked());
CephContext *cct = image_ctx.cct;
if (image_ctx.exclusive_lock != nullptr &&
!image_ctx.exclusive_lock->is_lock_owner()) {
ldout(cct, 1) << "lost exclusive lock during migrate" << dendl;
return -ERESTART;
}
start_async_op();
return 0;
}
private:
uint64_t m_object_size;
::SnapContext m_snapc;
uint64_t m_object_no;
io::AsyncOperation m_async_op;
void start_async_op() {
I &image_ctx = this->m_image_ctx;
assert(image_ctx.owner_lock.is_locked());
CephContext *cct = image_ctx.cct;
ldout(cct, 10) << dendl;
m_async_op.start_op(image_ctx);
if (!image_ctx.io_work_queue->writes_blocked()) {
migrate_object();
return;
}
auto ctx = create_async_context_callback(
image_ctx, create_context_callback<
C_MigrateObject<I>, &C_MigrateObject<I>::handle_start_async_op>(this));
m_async_op.finish_op();
image_ctx.io_work_queue->wait_on_writes_unblocked(ctx);
}
void handle_start_async_op(int r) {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
ldout(cct, 10) << "r=" << r << dendl;
if (r < 0) {
lderr(cct) << "failed to start async op: " << cpp_strerror(r) << dendl;
this->complete(r);
return;
}
RWLock::RLocker owner_locker(image_ctx.owner_lock);
start_async_op();
}
bool is_within_overlap_bounds() {
I &image_ctx = this->m_image_ctx;
RWLock::RLocker snap_locker(image_ctx.snap_lock);
auto overlap = std::min(image_ctx.size, image_ctx.migration_info.overlap);
return overlap > 0 &&
Striper::get_num_objects(image_ctx.layout, overlap) > m_object_no;
}
void migrate_object() {
I &image_ctx = this->m_image_ctx;
assert(image_ctx.owner_lock.is_locked());
CephContext *cct = image_ctx.cct;
auto ctx = create_context_callback<
C_MigrateObject<I>, &C_MigrateObject<I>::handle_migrate_object>(this);
if (is_within_overlap_bounds()) {
bufferlist bl;
string oid = image_ctx.get_object_name(m_object_no);
auto req = new io::ObjectWriteRequest<I>(&image_ctx, oid, m_object_no, 0,
std::move(bl), m_snapc, 0, {},
ctx);
ldout(cct, 20) << "copyup object req " << req << ", object_no "
<< m_object_no << dendl;
req->send();
} else {
assert(image_ctx.parent != nullptr);
auto req = deep_copy::ObjectCopyRequest<I>::create(
image_ctx.parent, image_ctx.parent->parent /* TODO */, &image_ctx,
image_ctx.migration_info.snap_map, m_object_no,
image_ctx.migration_info.flatten, ctx);
ldout(cct, 20) << "deep copy object req " << req << ", object_no "
<< m_object_no << dendl;
req->send();
}
}
void handle_migrate_object(int r) {
CephContext *cct = this->m_image_ctx.cct;
ldout(cct, 10) << "r=" << r << dendl;
m_async_op.finish_op();
this->complete(r);
}
};
} // anonymous namespace
template <typename I>
void MigrateRequest<I>::send_op() {
I &image_ctx = this->m_image_ctx;
assert(image_ctx.owner_lock.is_locked());
CephContext *cct = image_ctx.cct;
ldout(cct, 10) << dendl;
migrate_objects();
}
template <typename I>
bool MigrateRequest<I>::should_complete(int r) {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
ldout(cct, 10) << "r=" << r << dendl;
if (r < 0) {
lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl;
}
return true;
}
template <typename I>
void MigrateRequest<I>::migrate_objects() {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
assert(image_ctx.owner_lock.is_locked());
uint64_t overlap_objects = get_num_overlap_objects();
ldout(cct, 10) << "from 0 to " << overlap_objects << dendl;
auto ctx = create_context_callback<
MigrateRequest<I>, &MigrateRequest<I>::handle_migrate_objects>(this);
typename AsyncObjectThrottle<I>::ContextFactory context_factory(
boost::lambda::bind(boost::lambda::new_ptr<C_MigrateObject<I> >(),
boost::lambda::_1, &image_ctx, image_ctx.snapc, boost::lambda::_2));
AsyncObjectThrottle<I> *throttle = new AsyncObjectThrottle<I>(
this, image_ctx, context_factory, ctx, &m_prog_ctx, 0, overlap_objects);
throttle->start_ops(image_ctx.concurrent_management_ops);
}
template <typename I>
void MigrateRequest<I>::handle_migrate_objects(int r) {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
ldout(cct, 5) << "r=" << r << dendl;
if (r < 0) {
lderr(cct) << "failed to migrate objects: " << cpp_strerror(r) << dendl;
}
this->complete(r);
}
template <typename I>
uint64_t MigrateRequest<I>::get_num_overlap_objects() {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
ldout(cct, 10) << dendl;
RWLock::RLocker snap_locker(image_ctx.snap_lock);
RWLock::RLocker parent_locker(image_ctx.parent_lock);
auto overlap = image_ctx.migration_info.overlap;
return overlap > 0 ?
Striper::get_num_objects(image_ctx.layout, overlap) : 0;
}
} // namespace operation
} // namespace librbd
template class librbd::operation::MigrateRequest<librbd::ImageCtx>;

View File

@ -0,0 +1,69 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#ifndef CEPH_LIBRBD_OPERATION_MIGRATE_REQUEST_H
#define CEPH_LIBRBD_OPERATION_MIGRATE_REQUEST_H
#include "librbd/operation/Request.h"
#include "common/snap_types.h"
#include "librbd/Types.h"
namespace librbd {
class ImageCtx;
class ProgressContext;
namespace operation {
template <typename ImageCtxT = ImageCtx>
class MigrateRequest : public Request<ImageCtxT>
{
public:
MigrateRequest(ImageCtxT &image_ctx, Context *on_finish,
ProgressContext &prog_ctx)
: Request<ImageCtxT>(image_ctx, on_finish), m_prog_ctx(prog_ctx) {
}
protected:
void send_op() override;
bool should_complete(int r) override;
bool can_affect_io() const override {
return true;
}
journal::Event create_event(uint64_t op_tid) const override {
assert(0);
return journal::UnknownEvent();
}
private:
/**
* Migrate goes through the following state machine to copy objects
* from the parent (migrating source) image:
*
* @verbatim
*
* <start>
* |
* v
* MIGRATE_OBJECTS
* |
* v
* <finish>
*
* @endverbatim
*
*/
ProgressContext &m_prog_ctx;
void migrate_objects();
void handle_migrate_objects(int r);
uint64_t get_num_overlap_objects();
};
} // namespace operation
} // namespace librbd
extern template class librbd::operation::MigrateRequest<librbd::ImageCtx>;
#endif // CEPH_LIBRBD_OPERATION_MIGRATE_REQUEST_H

View File

@ -8,6 +8,7 @@ set(librbd_test
test_BlockGuard.cc
test_DeepCopy.cc
test_Groups.cc
test_Migration.cc
test_MirroringWatcher.cc
test_ObjectMap.cc
test_Operations.cc

View File

@ -40,14 +40,16 @@ template <>
struct RefreshParentRequest<MockRefreshImageCtx> {
static RefreshParentRequest* s_instance;
static RefreshParentRequest* create(MockRefreshImageCtx &mock_image_ctx,
const ParentInfo& parent_md,
const ParentInfo &parent_md,
const MigrationInfo &migration_info,
Context *on_finish) {
assert(s_instance != nullptr);
s_instance->on_finish = on_finish;
return s_instance;
}
static bool is_refresh_required(MockRefreshImageCtx &mock_image_ctx,
const ParentInfo& parent_md) {
const ParentInfo& parent_md,
const MigrationInfo &migration_info) {
assert(s_instance != nullptr);
return s_instance->is_refresh_required();
}
@ -134,6 +136,17 @@ public:
typedef RefreshParentRequest<MockRefreshImageCtx> MockRefreshParentRequest;
typedef io::ImageDispatchSpec<librbd::MockRefreshImageCtx> MockIoImageDispatchSpec;
void set_v1_migration_header(ImageCtx *ictx) {
bufferlist hdr;
ASSERT_EQ(0, read_header_bl(ictx->md_ctx, ictx->header_oid, hdr, nullptr));
ASSERT_TRUE(hdr.length() >= sizeof(rbd_obj_header_ondisk));
ASSERT_EQ(0, memcmp(RBD_HEADER_TEXT, hdr.c_str(), sizeof(RBD_HEADER_TEXT)));
bufferlist::iterator it = hdr.begin();
it.copy_in(sizeof(RBD_MIGRATE_HEADER_TEXT), RBD_MIGRATE_HEADER_TEXT);
ASSERT_EQ(0, ictx->md_ctx.write(ictx->header_oid, hdr, hdr.length(), 0));
}
void expect_set_require_lock(MockRefreshImageCtx &mock_image_ctx,
librbd::io::Direction direction, bool enabled) {
EXPECT_CALL(*mock_image_ctx.io_work_queue, set_require_lock(direction,
@ -201,6 +214,17 @@ public:
}
}
void expect_get_migration_header(MockRefreshImageCtx &mock_image_ctx, int r) {
auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
StrEq("migration_get"), _, _, _));
if (r < 0) {
expect.WillOnce(Return(r));
} else {
expect.WillOnce(DoDefault());
}
}
void expect_get_metadata(MockRefreshImageCtx &mock_image_ctx, int r) {
auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("metadata_list"), _, _, _));

View File

@ -357,10 +357,8 @@ public:
}
void expect_test_features(MockTestImageCtx &mock_image_ctx) {
if (m_mock_imctx->exclusive_lock != nullptr) {
EXPECT_CALL(mock_image_ctx, test_features(_))
.WillRepeatedly(TestFeatures(&mock_image_ctx));
}
EXPECT_CALL(mock_image_ctx, test_features(_))
.WillRepeatedly(TestFeatures(&mock_image_ctx));
}
void expect_set_journal_policy(MockTestImageCtx &mock_image_ctx) {
@ -391,6 +389,7 @@ TEST_F(TestMockImageRemoveRequest, SuccessV1) {
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
MockListWatchersRequest mock_list_watchers_request;
expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
@ -446,11 +445,16 @@ TEST_F(TestMockImageRemoveRequest, SuccessV2CloneV1) {
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
if (m_mock_imctx->exclusive_lock != nullptr) {
expect_test_features(*m_mock_imctx);
}
expect_set_journal_policy(*m_mock_imctx);
expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
expect_test_features(*m_mock_imctx);
MockListWatchersRequest mock_list_watchers_request;
expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
@ -499,11 +503,16 @@ TEST_F(TestMockImageRemoveRequest, SuccessV2CloneV2) {
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
if (m_mock_imctx->exclusive_lock != nullptr) {
expect_test_features(*m_mock_imctx);
}
expect_set_journal_policy(*m_mock_imctx);
expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
expect_test_features(*m_mock_imctx);
MockListWatchersRequest mock_list_watchers_request;
expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
@ -552,11 +561,14 @@ TEST_F(TestMockImageRemoveRequest, NotExistsV2) {
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
expect_test_features(*m_mock_imctx);
expect_set_journal_policy(*m_mock_imctx);
expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
expect_test_features(*m_mock_imctx);
MockListWatchersRequest mock_list_watchers_request;
expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);
@ -598,6 +610,7 @@ TEST_F(TestMockImageRemoveRequest, OperationsDisabled) {
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
expect_state_close(*m_mock_imctx);
C_SaferCond ctx;
@ -610,12 +623,31 @@ TEST_F(TestMockImageRemoveRequest, OperationsDisabled) {
ASSERT_EQ(-EROFS, ctx.wait());
}
TEST_F(TestMockImageRemoveRequest, Migration) {
m_mock_imctx->features |= RBD_FEATURE_MIGRATING;
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
expect_state_close(*m_mock_imctx);
C_SaferCond ctx;
librbd::NoOpProgressContext no_op;
ContextWQ op_work_queue;
MockRemoveRequest *req = MockRemoveRequest::create(
m_ioctx, m_image_name, "", true, false, no_op, &op_work_queue, &ctx);
req->send();
ASSERT_EQ(-EBUSY, ctx.wait());
}
TEST_F(TestMockImageRemoveRequest, Snapshots) {
m_mock_imctx->snap_info = {
{123, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, {}, {}, {}, {}, {}}}};
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
expect_state_close(*m_mock_imctx);
C_SaferCond ctx;
@ -643,11 +675,16 @@ TEST_F(TestMockImageRemoveRequest, AutoDeleteSnapshots) {
InSequence seq;
expect_state_open(*m_mock_imctx, 0);
expect_test_features(*m_mock_imctx);
if (m_mock_imctx->exclusive_lock != nullptr) {
expect_test_features(*m_mock_imctx);
}
expect_set_journal_policy(*m_mock_imctx);
expect_shut_down_exclusive_lock(*m_mock_imctx, *mock_exclusive_lock, 0);
expect_test_features(*m_mock_imctx);
MockListWatchersRequest mock_list_watchers_request;
expect_list_image_watchers(*m_mock_imctx, mock_list_watchers_request, 0);

View File

@ -111,7 +111,8 @@ struct MockImageCtx {
mirroring_replay_delay(image_ctx.mirroring_replay_delay),
non_blocking_aio(image_ctx.non_blocking_aio),
blkin_trace_all(image_ctx.blkin_trace_all),
enable_alloc_hint(image_ctx.enable_alloc_hint)
enable_alloc_hint(image_ctx.enable_alloc_hint),
ignore_migrating(image_ctx.ignore_migrating)
{
md_ctx.dup(image_ctx.md_ctx);
data_ctx.dup(image_ctx.data_ctx);
@ -264,6 +265,7 @@ struct MockImageCtx {
std::string id;
std::string name;
ParentInfo parent_md;
MigrationInfo migration_info;
char *format_string;
cls::rbd::GroupSpec group_spec;
@ -316,6 +318,7 @@ struct MockImageCtx {
bool non_blocking_aio;
bool blkin_trace_all;
bool enable_alloc_hint;
bool ignore_migrating;
};
} // namespace librbd

File diff suppressed because it is too large Load Diff

View File

@ -6783,6 +6783,129 @@ TEST_F(TestLibRBD, NamespacesPP) {
ASSERT_EQ("name3", names[0]);
}
TEST_F(TestLibRBD, Migration) {
bool old_format;
uint64_t features;
ASSERT_EQ(0, get_features(&old_format, &features));
rados_ioctx_t ioctx;
rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
BOOST_SCOPE_EXIT(&ioctx) {
rados_ioctx_destroy(ioctx);
} BOOST_SCOPE_EXIT_END;
int order = 0;
std::string name = get_temp_image_name();
uint64_t size = 2 << 20;
ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
rbd_image_options_t image_options;
rbd_image_options_create(&image_options);
BOOST_SCOPE_EXIT(&image_options) {
rbd_image_options_destroy(image_options);
} BOOST_SCOPE_EXIT_END;
ASSERT_EQ(0, rbd_migration_prepare(ioctx, name.c_str(), ioctx, name.c_str(),
image_options));
rbd_image_migration_status_t status;
ASSERT_EQ(0, rbd_migration_status(ioctx, name.c_str(), &status,
sizeof(status)));
ASSERT_EQ(status.source_pool_id, rados_ioctx_get_id(ioctx));
ASSERT_EQ(status.source_image_name, name);
if (old_format) {
ASSERT_EQ(status.source_image_id, string());
} else {
ASSERT_NE(status.source_image_id, string());
}
ASSERT_EQ(status.dest_pool_id, rados_ioctx_get_id(ioctx));
ASSERT_EQ(status.dest_image_name, name);
ASSERT_NE(status.dest_image_id, string());
ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
rbd_migration_status_cleanup(&status);
ASSERT_EQ(-EBUSY, rbd_remove(ioctx, name.c_str()));
ASSERT_EQ(0, rbd_migration_execute(ioctx, name.c_str()));
ASSERT_EQ(0, rbd_migration_status(ioctx, name.c_str(), &status,
sizeof(status)));
ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_EXECUTED);
rbd_migration_status_cleanup(&status);
ASSERT_EQ(0, rbd_migration_commit(ioctx, name.c_str()));
std::string new_name = get_temp_image_name();
ASSERT_EQ(0, rbd_migration_prepare(ioctx, name.c_str(), ioctx,
new_name.c_str(), image_options));
ASSERT_EQ(-EBUSY, rbd_remove(ioctx, new_name.c_str()));
ASSERT_EQ(0, rbd_migration_abort(ioctx, name.c_str()));
rbd_image_t image;
ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
EXPECT_EQ(0, rbd_close(image));
}
TEST_F(TestLibRBD, MigrationPP) {
bool old_format;
uint64_t features;
ASSERT_EQ(0, get_features(&old_format, &features));
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
int order = 0;
std::string name = get_temp_image_name();
uint64_t size = 2 << 20;
librbd::RBD rbd;
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
librbd::ImageOptions image_options;
ASSERT_EQ(0, rbd.migration_prepare(ioctx, name.c_str(), ioctx, name.c_str(),
image_options));
librbd::image_migration_status_t status;
ASSERT_EQ(0, rbd.migration_status(ioctx, name.c_str(), &status,
sizeof(status)));
ASSERT_EQ(status.source_pool_id, ioctx.get_id());
ASSERT_EQ(status.source_image_name, name);
if (old_format) {
ASSERT_EQ(status.source_image_id, "");
} else {
ASSERT_NE(status.source_image_id, "");
}
ASSERT_EQ(status.dest_pool_id, ioctx.get_id());
ASSERT_EQ(status.dest_image_name, name);
ASSERT_NE(status.dest_image_id, "");
ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
ASSERT_EQ(-EBUSY, rbd.remove(ioctx, name.c_str()));
ASSERT_EQ(0, rbd.migration_execute(ioctx, name.c_str()));
ASSERT_EQ(0, rbd.migration_status(ioctx, name.c_str(), &status,
sizeof(status)));
ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_EXECUTED);
ASSERT_EQ(0, rbd.migration_commit(ioctx, name.c_str()));
std::string new_name = get_temp_image_name();
ASSERT_EQ(0, rbd.migration_prepare(ioctx, name.c_str(), ioctx,
new_name.c_str(), image_options));
ASSERT_EQ(-EBUSY, rbd.remove(ioctx, new_name.c_str()));
ASSERT_EQ(0, rbd.migration_abort(ioctx, name.c_str()));
librbd::Image image;
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
}
// poorman's assert()
namespace ceph {
void __ceph_assert_fail(const char *assertion, const char *file, int line,

View File

@ -16,6 +16,7 @@ extern void register_test_image_watcher();
extern void register_test_internal();
extern void register_test_journal_entries();
extern void register_test_journal_replay();
extern void register_test_migration();
extern void register_test_mirroring();
extern void register_test_mirroring_watcher();
extern void register_test_object_map();
@ -34,6 +35,7 @@ int main(int argc, char **argv)
register_test_internal();
register_test_journal_entries();
register_test_journal_replay();
register_test_migration();
register_test_mirroring();
register_test_mirroring_watcher();
register_test_object_map();

View File

@ -1097,6 +1097,114 @@ TRACEPOINT_EVENT(librbd, rename_exit,
)
)
TRACEPOINT_EVENT(librbd, migration_prepare_enter,
TP_ARGS(
const char*, pool_name,
uint64_t, id,
const char*, image_name,
const char*, dest_pool_name,
uint64_t, dest_id,
const char*, dest_image_name,
void*, opts),
TP_FIELDS(
ctf_string(pool_name, pool_name)
ctf_integer(uint64_t, id, id)
ctf_string(image_name, image_name)
ctf_string(dest_pool_name, dest_pool_name)
ctf_integer(uint64_t, dest_id, dest_id)
ctf_string(dest_image_name, dest_image_name)
ctf_integer_hex(void*, opts, opts)
)
)
TRACEPOINT_EVENT(librbd, migration_prepare_exit,
TP_ARGS(
int, retval),
TP_FIELDS(
ctf_integer(int, retval, retval)
)
)
TRACEPOINT_EVENT(librbd, migration_execute_enter,
TP_ARGS(
const char*, pool_name,
int64_t, pool_id,
const char*, image_name),
TP_FIELDS(
ctf_string(pool_name, pool_name)
ctf_integer(int64_t, pool_id, pool_id)
ctf_string(image_name, image_name)
)
)
TRACEPOINT_EVENT(librbd, migration_execute_exit,
TP_ARGS(
int, retval),
TP_FIELDS(
ctf_integer(int, retval, retval)
)
)
TRACEPOINT_EVENT(librbd, migration_abort_enter,
TP_ARGS(
const char*, pool_name,
int64_t, pool_id,
const char*, image_name),
TP_FIELDS(
ctf_string(pool_name, pool_name)
ctf_integer(int64_t, pool_id, pool_id)
ctf_string(image_name, image_name)
)
)
TRACEPOINT_EVENT(librbd, migration_abort_exit,
TP_ARGS(
int, retval),
TP_FIELDS(
ctf_integer(int, retval, retval)
)
)
TRACEPOINT_EVENT(librbd, migration_commit_enter,
TP_ARGS(
const char*, pool_name,
int64_t, pool_id,
const char*, image_name),
TP_FIELDS(
ctf_string(pool_name, pool_name)
ctf_integer(int64_t, pool_id, pool_id)
ctf_string(image_name, image_name)
)
)
TRACEPOINT_EVENT(librbd, migration_commit_exit,
TP_ARGS(
int, retval),
TP_FIELDS(
ctf_integer(int, retval, retval)
)
)
TRACEPOINT_EVENT(librbd, migration_status_enter,
TP_ARGS(
const char*, pool_name,
int64_t, pool_id,
const char*, image_name),
TP_FIELDS(
ctf_string(pool_name, pool_name)
ctf_integer(int64_t, pool_id, pool_id)
ctf_string(image_name, image_name)
)
)
TRACEPOINT_EVENT(librbd, migration_status_exit,
TP_ARGS(
int, retval),
TP_FIELDS(
ctf_integer(int, retval, retval)
)
)
TRACEPOINT_EVENT(librbd, discard_enter,
TP_ARGS(
void*, imagectx,