mirror of
https://github.com/ceph/ceph
synced 2025-01-01 08:32:24 +00:00
cls/rbd: methods to set/get/remove image migration header
Signed-off-by: Mykola Golub <mgolub@suse.com>
This commit is contained in:
parent
2bec876995
commit
2f7c4ae3fc
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user