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
/**
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)
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;
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",
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);
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);
<< 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
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
#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 | \
#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
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 | \
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 | \
#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)
{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");
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();
+}
#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)