cls/rbd: methods to set/get/remove image migration header

Signed-off-by: Mykola Golub <mgolub@suse.com>
This commit is contained in:
Mykola Golub 2017-11-22 15:47:45 +02:00 committed by Jason Dillaman
parent 2bec876995
commit 2f7c4ae3fc
9 changed files with 723 additions and 5 deletions

View File

@ -278,6 +278,184 @@ int snapshot_iterate(cls_method_context_t hctx, L& lambda) {
return 0;
}
int set_migration(cls_method_context_t hctx,
const cls::rbd::MigrationSpec &migration_spec, bool init) {
if (init) {
bufferlist bl;
int r = cls_cxx_map_get_val(hctx, "migration", &bl);
if (r != -ENOENT) {
if (r == 0) {
CLS_LOG(10, "migration already set");
return -EEXIST;
}
CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str());
return r;
}
uint64_t features = 0;
r = read_key(hctx, "features", &features);
if (r == -ENOENT) {
CLS_LOG(20, "no features, assuming v1 format");
bufferlist header;
r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header);
if (r < 0) {
CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str());
return r;
}
if (header.length() != sizeof(RBD_HEADER_TEXT)) {
CLS_ERR("unrecognized v1 header format");
return -ENXIO;
}
if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) != 0) {
if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(),
header.length()) == 0) {
CLS_LOG(10, "migration already set");
return -EEXIST;
} else {
CLS_ERR("unrecognized v1 header format");
return -ENXIO;
}
}
if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) {
CLS_LOG(10, "v1 format image can only be migration source");
return -EINVAL;
}
header.clear();
header.append(RBD_MIGRATE_HEADER_TEXT);
r = cls_cxx_write(hctx, 0, header.length(), &header);
if (r < 0) {
CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str());
return r;
}
} else if (r < 0) {
CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
return r;
} else if ((features & RBD_FEATURE_MIGRATING) != 0ULL) {
if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_DST) {
CLS_LOG(10, "migrating feature already set");
return -EEXIST;
}
} else {
features |= RBD_FEATURE_MIGRATING;
bl.clear();
encode(features, bl);
r = cls_cxx_map_set_val(hctx, "features", &bl);
if (r < 0) {
CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
return r;
}
}
}
bufferlist bl;
encode(migration_spec, bl);
int r = cls_cxx_map_set_val(hctx, "migration", &bl);
if (r < 0) {
CLS_ERR("error setting migration: %s", cpp_strerror(r).c_str());
return r;
}
return 0;
}
int read_migration(cls_method_context_t hctx,
cls::rbd::MigrationSpec *migration_spec) {
uint64_t features = 0;
int r = read_key(hctx, "features", &features);
if (r == -ENOENT) {
CLS_LOG(20, "no features, assuming v1 format");
bufferlist header;
r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header);
if (r < 0) {
CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str());
return r;
}
if (header.length() != sizeof(RBD_HEADER_TEXT)) {
CLS_ERR("unrecognized v1 header format");
return -ENXIO;
}
if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) {
if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) {
CLS_LOG(10, "migration feature not set");
return -EINVAL;
} else {
CLS_ERR("unrecognized v1 header format");
return -ENXIO;
}
}
if (migration_spec->header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) {
CLS_LOG(10, "v1 format image can only be migration source");
return -EINVAL;
}
} else if (r < 0) {
CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
return r;
} else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) {
CLS_LOG(10, "migration feature not set");
return -EINVAL;
}
r = read_key(hctx, "migration", migration_spec);
if (r < 0) {
CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str());
return r;
}
return 0;
}
int remove_migration(cls_method_context_t hctx) {
int r = remove_key(hctx, "migration");
if (r < 0) {
return r;
}
uint64_t features = 0;
r = read_key(hctx, "features", &features);
if (r == -ENOENT) {
CLS_LOG(20, "no features, assuming v1 format");
bufferlist header;
r = cls_cxx_read(hctx, 0, sizeof(RBD_MIGRATE_HEADER_TEXT), &header);
if (header.length() != sizeof(RBD_MIGRATE_HEADER_TEXT)) {
CLS_ERR("unrecognized v1 header format");
return -ENXIO;
}
if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) {
if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) {
CLS_LOG(10, "migration feature not set");
return -EINVAL;
} else {
CLS_ERR("unrecognized v1 header format");
return -ENXIO;
}
}
header.clear();
header.append(RBD_HEADER_TEXT);
r = cls_cxx_write(hctx, 0, header.length(), &header);
if (r < 0) {
CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str());
return r;
}
} else if (r < 0) {
CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
return r;
} else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) {
CLS_LOG(10, "migrating feature not set");
} else {
features &= ~RBD_FEATURE_MIGRATING;
bufferlist bl;
encode(features, bl);
r = cls_cxx_map_set_val(hctx, "features", &bl);
if (r < 0) {
CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
return r;
}
}
return 0;
}
} // namespace image
/**
@ -3449,6 +3627,114 @@ int children_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
return 0;
}
/**
* Set image migration.
*
* Input:
* @param migration_spec (cls::rbd::MigrationSpec) image migration spec
*
* Output:
*
* @returns 0 on success, negative error code on failure
*/
int migration_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
cls::rbd::MigrationSpec migration_spec;
try {
auto it = in->cbegin();
decode(migration_spec, it);
} catch (const buffer::error &err) {
return -EINVAL;
}
int r = image::set_migration(hctx, migration_spec, true);
if (r < 0) {
return r;
}
return 0;
}
/**
* Set image migration state.
*
* Input:
* @param state (cls::rbd::MigrationState) migration state
* @param description (std::string) migration state description
*
* Output:
*
* @returns 0 on success, negative error code on failure
*/
int migration_set_state(cls_method_context_t hctx, bufferlist *in,
bufferlist *out) {
cls::rbd::MigrationState state;
std::string description;
try {
auto it = in->cbegin();
decode(state, it);
decode(description, it);
} catch (const buffer::error &err) {
return -EINVAL;
}
cls::rbd::MigrationSpec migration_spec;
int r = image::read_migration(hctx, &migration_spec);
if (r < 0) {
return r;
}
migration_spec.state = state;
migration_spec.state_description = description;
r = image::set_migration(hctx, migration_spec, false);
if (r < 0) {
return r;
}
return 0;
}
/**
* Get image migration spec.
*
* Input:
*
* Output:
* @param migration_spec (cls::rbd::MigrationSpec) image migration spec
*
* @returns 0 on success, negative error code on failure
*/
int migration_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
cls::rbd::MigrationSpec migration_spec;
int r = image::read_migration(hctx, &migration_spec);
if (r < 0) {
return r;
}
encode(migration_spec, *out);
return 0;
}
/**
* Remove image migration spec.
*
* Input:
*
* Output:
*
* @returns 0 on success, negative error code on failure
*/
int migration_remove(cls_method_context_t hctx, bufferlist *in,
bufferlist *out) {
int r = image::remove_migration(hctx);
if (r < 0) {
return r;
}
return 0;
}
/****************************** Old format *******************************/
int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
@ -6371,6 +6657,10 @@ CLS_INIT(rbd)
cls_method_handle_t h_child_attach;
cls_method_handle_t h_child_detach;
cls_method_handle_t h_children_list;
cls_method_handle_t h_migration_set;
cls_method_handle_t h_migration_set_state;
cls_method_handle_t h_migration_get;
cls_method_handle_t h_migration_remove;
cls_method_handle_t h_old_snapshots_list;
cls_method_handle_t h_old_snapshot_add;
cls_method_handle_t h_old_snapshot_remove;
@ -6536,6 +6826,18 @@ CLS_INIT(rbd)
cls_register_cxx_method(h_class, "children_list",
CLS_METHOD_RD,
children_list, &h_children_list);
cls_register_cxx_method(h_class, "migration_set",
CLS_METHOD_RD | CLS_METHOD_WR,
migration_set, &h_migration_set);
cls_register_cxx_method(h_class, "migration_set_state",
CLS_METHOD_RD | CLS_METHOD_WR,
migration_set_state, &h_migration_set_state);
cls_register_cxx_method(h_class, "migration_get",
CLS_METHOD_RD,
migration_get, &h_migration_get);
cls_register_cxx_method(h_class, "migration_remove",
CLS_METHOD_RD | CLS_METHOD_WR,
migration_remove, &h_migration_remove);
/* methods for the rbd_children object */
cls_register_cxx_method(h_class, "add_child",

View File

@ -1591,6 +1591,82 @@ namespace librbd {
return 0;
}
int migration_set(librados::IoCtx *ioctx, const std::string &oid,
const cls::rbd::MigrationSpec &migration_spec) {
librados::ObjectWriteOperation op;
migration_set(&op, migration_spec);
return ioctx->operate(oid, &op);
}
void migration_set(librados::ObjectWriteOperation *op,
const cls::rbd::MigrationSpec &migration_spec) {
bufferlist bl;
encode(migration_spec, bl);
op->exec("rbd", "migration_set", bl);
}
int migration_set_state(librados::IoCtx *ioctx, const std::string &oid,
cls::rbd::MigrationState state,
const std::string &description) {
librados::ObjectWriteOperation op;
migration_set_state(&op, state, description);
return ioctx->operate(oid, &op);
}
void migration_set_state(librados::ObjectWriteOperation *op,
cls::rbd::MigrationState state,
const std::string &description) {
bufferlist bl;
encode(state, bl);
encode(description, bl);
op->exec("rbd", "migration_set_state", bl);
}
void migration_get_start(librados::ObjectReadOperation *op) {
bufferlist bl;
op->exec("rbd", "migration_get", bl);
}
int migration_get_finish(bufferlist::const_iterator *it,
cls::rbd::MigrationSpec *migration_spec) {
try {
decode(*migration_spec, *it);
} catch (const buffer::error &err) {
return -EBADMSG;
}
return 0;
}
int migration_get(librados::IoCtx *ioctx, const std::string &oid,
cls::rbd::MigrationSpec *migration_spec) {
librados::ObjectReadOperation op;
migration_get_start(&op);
bufferlist out_bl;
int r = ioctx->operate(oid, &op, &out_bl);
if (r < 0) {
return r;
}
auto iter = out_bl.cbegin();
r = migration_get_finish(&iter, migration_spec);
if (r < 0) {
return r;
}
return 0;
}
int migration_remove(librados::IoCtx *ioctx, const std::string &oid) {
librados::ObjectWriteOperation op;
migration_remove(&op);
return ioctx->operate(oid, &op);
}
void migration_remove(librados::ObjectWriteOperation *op) {
bufferlist bl;
op->exec("rbd", "migration_remove", bl);
}
void mirror_uuid_get_start(librados::ObjectReadOperation *op) {
bufferlist bl;
op->exec("rbd", "mirror_uuid_get", bl);

View File

@ -258,6 +258,24 @@ namespace librbd {
snapid_t snap_id,
cls::rbd::ChildImageSpecs *child_images);
int migration_set(librados::IoCtx *ioctx, const std::string &oid,
const cls::rbd::MigrationSpec &migration_spec);
void migration_set(librados::ObjectWriteOperation *op,
const cls::rbd::MigrationSpec &migration_spec);
int migration_set_state(librados::IoCtx *ioctx, const std::string &oid,
cls::rbd::MigrationState state,
const std::string &description);
void migration_set_state(librados::ObjectWriteOperation *op,
cls::rbd::MigrationState state,
const std::string &description);
void migration_get_start(librados::ObjectReadOperation *op);
int migration_get_finish(bufferlist::const_iterator *it,
cls::rbd::MigrationSpec *migration_spec);
int migration_get(librados::IoCtx *ioctx, const std::string &oid,
cls::rbd::MigrationSpec *migration_spec);
int migration_remove(librados::IoCtx *ioctx, const std::string &oid);
void migration_remove(librados::ObjectWriteOperation *op);
// operations on rbd_id objects
void get_id_start(librados::ObjectReadOperation *op);
int get_id_finish(bufferlist::const_iterator *it, std::string *id);

View File

@ -732,5 +732,121 @@ std::ostream& operator<<(std::ostream& os,
<< image_map.mapped_time << "]";
}
std::ostream& operator<<(std::ostream& os,
const MigrationHeaderType& type) {
switch (type) {
case MIGRATION_HEADER_TYPE_SRC:
os << "source";
break;
case MIGRATION_HEADER_TYPE_DST:
os << "destination";
break;
default:
os << "unknown (" << static_cast<uint32_t>(type) << ")";
break;
}
return os;
}
std::ostream& operator<<(std::ostream& os,
const MigrationState& migration_state) {
switch (migration_state) {
case MIGRATION_STATE_ERROR:
os << "error";
break;
case MIGRATION_STATE_PREPARING:
os << "preparing";
break;
case MIGRATION_STATE_PREPARED:
os << "prepared";
break;
case MIGRATION_STATE_EXECUTING:
os << "executing";
break;
case MIGRATION_STATE_EXECUTED:
os << "executed";
break;
default:
os << "unknown (" << static_cast<uint32_t>(migration_state) << ")";
break;
}
return os;
}
void MigrationSpec::encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
encode(header_type, bl);
encode(pool_id, bl);
encode(image_name, bl);
encode(image_id, bl);
encode(snap_seqs, bl);
encode(overlap, bl);
encode(flatten, bl);
encode(mirroring, bl);
encode(state, bl);
encode(state_description, bl);
ENCODE_FINISH(bl);
}
void MigrationSpec::decode(bufferlist::const_iterator& bl) {
DECODE_START(1, bl);
decode(header_type, bl);
decode(pool_id, bl);
decode(image_name, bl);
decode(image_id, bl);
decode(snap_seqs, bl);
decode(overlap, bl);
decode(flatten, bl);
decode(mirroring, bl);
decode(state, bl);
decode(state_description, bl);
DECODE_FINISH(bl);
}
std::ostream& operator<<(std::ostream& os,
const std::map<uint64_t, uint64_t>& snap_seqs) {
os << "{";
size_t count = 0;
for (auto &it : snap_seqs) {
os << (count++ > 0 ? ", " : "") << "(" << it.first << ", " << it.second
<< ")";
}
os << "}";
return os;
}
void MigrationSpec::dump(Formatter *f) const {
f->dump_stream("header_type") << header_type;
f->dump_int("pool_id", pool_id);
f->dump_string("image_name", image_name);
f->dump_string("image_id", image_id);
f->dump_stream("snap_seqs") << snap_seqs;
f->dump_unsigned("overlap", overlap);
f->dump_bool("mirroring", mirroring);
}
void MigrationSpec::generate_test_instances(std::list<MigrationSpec*> &o) {
o.push_back(new MigrationSpec());
o.push_back(new MigrationSpec(MIGRATION_HEADER_TYPE_SRC, 1, "image_name",
"image_id", {{1, 2}}, 123, true, true,
MIGRATION_STATE_PREPARED, "description"));
}
std::ostream& operator<<(std::ostream& os,
const MigrationSpec& migration_spec) {
os << "["
<< "header_type=" << migration_spec.header_type << ", "
<< "pool_id=" << migration_spec.pool_id << ", "
<< "image_name=" << migration_spec.image_name << ", "
<< "image_id=" << migration_spec.image_id << ", "
<< "snap_seqs=" << migration_spec.snap_seqs << ", "
<< "overlap=" << migration_spec.overlap << ", "
<< "flatten=" << migration_spec.flatten << ", "
<< "mirroring=" << migration_spec.mirroring << ", "
<< "state=" << migration_spec.state << ", "
<< "state_description=" << migration_spec.state_description << "]";
return os;
}
} // namespace rbd
} // namespace cls

View File

@ -587,6 +587,90 @@ std::ostream& operator<<(std::ostream& os, const MirrorImageMap &image_map);
WRITE_CLASS_ENCODER(MirrorImageMap);
enum MigrationHeaderType {
MIGRATION_HEADER_TYPE_SRC = 1,
MIGRATION_HEADER_TYPE_DST = 2,
};
inline void encode(const MigrationHeaderType &type, bufferlist& bl) {
using ceph::encode;
encode(static_cast<uint8_t>(type), bl);
}
inline void decode(MigrationHeaderType &type, bufferlist::const_iterator& it) {
uint8_t int_type;
using ceph::decode;
decode(int_type, it);
type = static_cast<MigrationHeaderType>(int_type);
}
enum MigrationState {
MIGRATION_STATE_ERROR = 0,
MIGRATION_STATE_PREPARING = 1,
MIGRATION_STATE_PREPARED = 2,
MIGRATION_STATE_EXECUTING = 3,
MIGRATION_STATE_EXECUTED = 4,
};
inline void encode(const MigrationState &state, bufferlist& bl) {
using ceph::encode;
encode(static_cast<uint8_t>(state), bl);
}
inline void decode(MigrationState &state, bufferlist::const_iterator& it) {
uint8_t int_state;
using ceph::decode;
decode(int_state, it);
state = static_cast<MigrationState>(int_state);
}
std::ostream& operator<<(std::ostream& os,
const MigrationState& migration_state);
struct MigrationSpec {
MigrationHeaderType header_type = MIGRATION_HEADER_TYPE_SRC;
int64_t pool_id = -1;
std::string image_name;
std::string image_id;
std::map<uint64_t, uint64_t> snap_seqs;
uint64_t overlap = 0;
bool flatten = false;
bool mirroring = false;
MigrationState state = MIGRATION_STATE_ERROR;
std::string state_description;
MigrationSpec() {
}
MigrationSpec(MigrationHeaderType header_type, int64_t pool_id,
const std::string &image_name, const std::string &image_id,
const std::map<uint64_t, uint64_t> &snap_seqs, uint64_t overlap,
bool mirroring, bool flatten, MigrationState state,
const std::string &state_description)
: header_type(header_type), pool_id(pool_id), image_name(image_name),
image_id(image_id), snap_seqs(snap_seqs), overlap(overlap),
flatten(flatten), mirroring(mirroring), state(state),
state_description(state_description) {
}
void encode(bufferlist &bl) const;
void decode(bufferlist::const_iterator& it);
void dump(Formatter *f) const;
static void generate_test_instances(std::list<MigrationSpec*> &o);
inline bool operator==(const MigrationSpec& ms) const {
return header_type == ms.header_type && pool_id == ms.pool_id &&
image_name == ms.image_name && image_id == ms.image_id &&
snap_seqs == ms.snap_seqs && overlap == ms.overlap &&
flatten == ms.flatten && mirroring == ms.mirroring && state == ms.state &&
state_description == ms.state_description;
}
};
std::ostream& operator<<(std::ostream& os, const MigrationSpec& migration_spec);
WRITE_CLASS_ENCODER(MigrationSpec);
} // namespace rbd
} // namespace cls

View File

@ -10,6 +10,7 @@
#define RBD_FEATURE_JOURNALING (1ULL<<6)
#define RBD_FEATURE_DATA_POOL (1ULL<<7)
#define RBD_FEATURE_OPERATIONS (1ULL<<8)
#define RBD_FEATURE_MIGRATING (1ULL<<9)
#define RBD_FEATURES_DEFAULT (RBD_FEATURE_LAYERING | \
RBD_FEATURE_EXCLUSIVE_LOCK | \
@ -26,6 +27,7 @@
#define RBD_FEATURE_NAME_JOURNALING "journaling"
#define RBD_FEATURE_NAME_DATA_POOL "data-pool"
#define RBD_FEATURE_NAME_OPERATIONS "operations"
#define RBD_FEATURE_NAME_MIGRATING "migrating"
/// features that make an image inaccessible for read or write by
/// clients that don't understand them
@ -40,7 +42,8 @@
RBD_FEATURE_FAST_DIFF | \
RBD_FEATURE_DEEP_FLATTEN | \
RBD_FEATURE_JOURNALING | \
RBD_FEATURE_OPERATIONS)
RBD_FEATURE_OPERATIONS | \
RBD_FEATURE_MIGRATING)
#define RBD_FEATURES_ALL (RBD_FEATURE_LAYERING | \
RBD_FEATURE_STRIPINGV2 | \
@ -50,7 +53,8 @@
RBD_FEATURE_DEEP_FLATTEN | \
RBD_FEATURE_JOURNALING | \
RBD_FEATURE_DATA_POOL | \
RBD_FEATURE_OPERATIONS)
RBD_FEATURE_OPERATIONS | \
RBD_FEATURE_MIGRATING)
/// features that may be dynamically enabled or disabled
#define RBD_FEATURES_MUTABLE (RBD_FEATURE_EXCLUSIVE_LOCK | \
@ -72,10 +76,12 @@
#define RBD_FEATURES_IMPLICIT_ENABLE (RBD_FEATURE_STRIPINGV2 | \
RBD_FEATURE_DATA_POOL | \
RBD_FEATURE_FAST_DIFF | \
RBD_FEATURE_OPERATIONS)
RBD_FEATURE_OPERATIONS | \
RBD_FEATURE_MIGRATING)
/// features that cannot be controlled by the user
#define RBD_FEATURES_INTERNAL (RBD_FEATURE_OPERATIONS)
#define RBD_FEATURES_INTERNAL (RBD_FEATURE_OPERATIONS | \
RBD_FEATURE_MIGRATING)
#define RBD_OPERATION_FEATURE_CLONE_PARENT (1ULL<<0)
#define RBD_OPERATION_FEATURE_CLONE_CHILD (1ULL<<1)

View File

@ -20,7 +20,7 @@ static const std::map<std::string, uint64_t> RBD_FEATURE_MAP = {
{RBD_FEATURE_NAME_JOURNALING, RBD_FEATURE_JOURNALING},
{RBD_FEATURE_NAME_DATA_POOL, RBD_FEATURE_DATA_POOL},
};
static_assert((RBD_FEATURE_OPERATIONS << 1) > RBD_FEATURES_ALL,
static_assert((RBD_FEATURE_MIGRATING << 1) > RBD_FEATURES_ALL,
"new RBD feature added");

View File

@ -2764,3 +2764,118 @@ TEST_F(TestClsRbd, namespace_methods)
ASSERT_EQ(0, namespace_list(&ioctx, name1, 1, &entries));
ASSERT_TRUE(entries.empty());
}
TEST_F(TestClsRbd, migration)
{
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
string oid = get_temp_image_name();
ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
cls::rbd::MigrationSpec migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST, 1,
"name", "id", {}, 0, false, false,
cls::rbd::MIGRATION_STATE_PREPARING,
"123");
cls::rbd::MigrationSpec read_migration_spec;
ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
uint64_t features;
ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
ASSERT_EQ(0U, features);
ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
ASSERT_EQ(migration_spec, read_migration_spec);
ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
ASSERT_EQ(RBD_FEATURE_MIGRATING, features);
ASSERT_EQ(-EEXIST, migration_set(&ioctx, oid, migration_spec));
migration_spec.state = cls::rbd::MIGRATION_STATE_PREPARED;
migration_spec.state_description = "456";
ASSERT_EQ(0, migration_set_state(&ioctx, oid, migration_spec.state,
migration_spec.state_description));
ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
ASSERT_EQ(migration_spec, read_migration_spec);
ASSERT_EQ(0, migration_remove(&ioctx, oid));
ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
ASSERT_EQ(0U, features);
ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
ASSERT_EQ(-EINVAL, migration_set_state(&ioctx, oid, migration_spec.state,
migration_spec.state_description));
migration_spec.header_type = cls::rbd::MIGRATION_HEADER_TYPE_SRC;
ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
ASSERT_EQ(RBD_FEATURE_MIGRATING, features);
ASSERT_EQ(0, migration_remove(&ioctx, oid));
ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
ASSERT_EQ(0U, features);
ioctx.close();
}
TEST_F(TestClsRbd, migration_v1)
{
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
bufferlist header;
header.append(RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT));
string oid = get_temp_image_name();
ASSERT_EQ(0, ioctx.write(oid, header, header.length(), 0));
cls::rbd::MigrationSpec migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST, 1,
"name", "id", {}, 0, false, false,
cls::rbd::MIGRATION_STATE_PREPARING,
"123");
cls::rbd::MigrationSpec read_migration_spec;
ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
// v1 format image can only be migration source
ASSERT_EQ(-EINVAL, migration_set(&ioctx, oid, migration_spec));
migration_spec.header_type = cls::rbd::MIGRATION_HEADER_TYPE_SRC;
ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
ASSERT_EQ(migration_spec, read_migration_spec);
header.clear();
ASSERT_EQ(static_cast<int>(sizeof(RBD_MIGRATE_HEADER_TEXT)),
ioctx.read(oid, header, sizeof(RBD_MIGRATE_HEADER_TEXT), 0));
ASSERT_STREQ(RBD_MIGRATE_HEADER_TEXT, header.c_str());
ASSERT_EQ(-EEXIST, migration_set(&ioctx, oid, migration_spec));
migration_spec.state = cls::rbd::MIGRATION_STATE_PREPARED;
migration_spec.state_description = "456";
ASSERT_EQ(0, migration_set_state(&ioctx, oid, migration_spec.state,
migration_spec.state_description));
ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
ASSERT_EQ(migration_spec, read_migration_spec);
ASSERT_EQ(0, migration_remove(&ioctx, oid));
ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
ASSERT_EQ(-EINVAL, migration_set_state(&ioctx, oid, migration_spec.state,
migration_spec.state_description));
header.clear();
ASSERT_EQ(static_cast<int>(sizeof(RBD_HEADER_TEXT)),
ioctx.read(oid, header, sizeof(RBD_HEADER_TEXT), 0));
ASSERT_STREQ(RBD_HEADER_TEXT, header.c_str());
ioctx.close();
}

View File

@ -465,6 +465,7 @@ TYPE(cls_rbd_snap)
#include "cls/rbd/cls_rbd_types.h"
TYPE(cls::rbd::ChildImageSpec)
TYPE(cls::rbd::MigrationSpec)
TYPE(cls::rbd::MirrorPeer)
TYPE(cls::rbd::MirrorImage)
TYPE(cls::rbd::MirrorImageMap)