return 0;
}
+/**
+ * Ensure writer snapc state
+ *
+ * Input:
+ * @param snap id (uint64_t) snap context sequence id
+ * @param state (cls::rbd::AssertSnapcSeqState) snap context state
+ *
+ * Output:
+ * @returns -ERANGE if assertion fails
+ * @returns 0 on success, negative error code on failure
+ */
+int assert_snapc_seq(cls_method_context_t hctx, bufferlist *in,
+ bufferlist *out)
+{
+ uint64_t snapc_seq;
+ cls::rbd::AssertSnapcSeqState state;
+ try {
+ auto it = in->cbegin();
+ decode(snapc_seq, it);
+ decode(state, it);
+ } catch (const buffer::error &err) {
+ return -EINVAL;
+ }
+
+ uint64_t snapset_seq;
+ int r = cls_get_snapset_seq(hctx, &snapset_seq);
+ if (r < 0 && r != -ENOENT) {
+ return r;
+ }
+
+ switch (state) {
+ case cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ:
+ return (r == -ENOENT || snapc_seq > snapset_seq) ? 0 : -ERANGE;
+ case cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ:
+ return (r == -ENOENT || snapc_seq > snapset_seq) ? -ERANGE : 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
/****************************** Old format *******************************/
int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
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_assert_snapc_seq;
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, "migration_remove",
CLS_METHOD_RD | CLS_METHOD_WR,
migration_remove, &h_migration_remove);
+ cls_register_cxx_method(h_class, "assert_snapc_seq",
+ CLS_METHOD_RD | CLS_METHOD_WR,
+ assert_snapc_seq,
+ &h_assert_snapc_seq);
/* methods for the rbd_children object */
cls_register_cxx_method(h_class, "add_child",
op->exec("rbd", "migration_remove", bl);
}
+ int assert_snapc_seq(librados::IoCtx *ioctx, const std::string &oid,
+ uint64_t snapc_seq,
+ cls::rbd::AssertSnapcSeqState state) {
+ librados::ObjectWriteOperation op;
+ assert_snapc_seq(&op, snapc_seq, state);
+ return ioctx->operate(oid, &op);
+ }
+
+ void assert_snapc_seq(librados::ObjectWriteOperation *op,
+ uint64_t snapc_seq,
+ cls::rbd::AssertSnapcSeqState state) {
+ bufferlist bl;
+ encode(snapc_seq, bl);
+ encode(state, bl);
+ op->exec("rbd", "assert_snapc_seq", bl);
+ }
+
void mirror_uuid_get_start(librados::ObjectReadOperation *op) {
bufferlist bl;
op->exec("rbd", "mirror_uuid_get", bl);
int migration_remove(librados::IoCtx *ioctx, const std::string &oid);
void migration_remove(librados::ObjectWriteOperation *op);
+ int assert_snapc_seq(librados::IoCtx *ioctx, const std::string &oid,
+ uint64_t snapc_seq,
+ cls::rbd::AssertSnapcSeqState state);
+ void assert_snapc_seq(librados::ObjectWriteOperation *op,
+ uint64_t snapc_seq,
+ cls::rbd::AssertSnapcSeqState state);
+
// operations on rbd_id objects
void get_id_start(librados::ObjectReadOperation *op);
int get_id_finish(bufferlist::const_iterator *it, std::string *id);
return os;
}
+std::ostream& operator<<(std::ostream& os, const AssertSnapcSeqState& state) {
+ switch (state) {
+ case ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ:
+ os << "gt";
+ break;
+ case ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ:
+ os << "le";
+ break;
+ default:
+ os << "unknown (" << static_cast<uint32_t>(state) << ")";
+ break;
+ }
+ return os;
+}
+
} // namespace rbd
} // namespace cls
WRITE_CLASS_ENCODER(MigrationSpec);
+enum AssertSnapcSeqState {
+ ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ = 0,
+ ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ = 1,
+};
+
+inline void encode(const AssertSnapcSeqState &state, bufferlist& bl) {
+ using ceph::encode;
+ encode(static_cast<uint8_t>(state), bl);
+}
+
+inline void decode(AssertSnapcSeqState &state, bufferlist::const_iterator& it) {
+ uint8_t int_state;
+ using ceph::decode;
+ decode(int_state, it);
+ state = static_cast<AssertSnapcSeqState>(int_state);
+}
+
+std::ostream& operator<<(std::ostream& os, const AssertSnapcSeqState& state);
+
} // namespace rbd
} // namespace cls
*s = buf;
}
+int cls_get_snapset_seq(cls_method_context_t hctx, uint64_t *snap_seq) {
+ PrimaryLogPG::OpContext *ctx = *(PrimaryLogPG::OpContext **)hctx;
+ if (!ctx->new_obs.exists) {
+ return -ENOENT;
+ }
+ *snap_seq = ctx->obc->ssc->snapset.seq;
+ return 0;
+}
+
int cls_log(int level, const char *format, ...)
{
int size = 256;
/* helpers */
extern void cls_cxx_subop_version(cls_method_context_t hctx, string *s);
+extern int cls_get_snapset_seq(cls_method_context_t hctx, uint64_t *snap_seq);
+
/* These are also defined in rados.h and librados.h. Keep them in sync! */
#define CEPH_OSD_TMAP_HDR 'h'
#define CEPH_OSD_TMAP_SET 's'
ioctx.close();
}
+
+TEST_F(TestClsRbd, assert_snapc_seq)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+ string oid = get_temp_image_name();
+
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, 0,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, 0,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0, ioctx.create(oid, true));
+
+ uint64_t snapc_seq = 0;
+
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ std::vector<uint64_t> snaps;
+ snaps.push_back(CEPH_NOSNAP);
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_create(&snaps.back()));
+ snapc_seq = snaps[0];
+
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_set_write_ctx(snaps[0], snaps));
+ bufferlist bl;
+ bl.append("foo");
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0,
+ assert_snapc_seq(&ioctx, oid, snapc_seq + 1,
+ cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ));
+ ASSERT_EQ(-ERANGE,
+ assert_snapc_seq(&ioctx, oid, snapc_seq + 1,
+ cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ));
+
+ ASSERT_EQ(0, ioctx.selfmanaged_snap_remove(snapc_seq));
+}
return CEPH_FEATURES_SUPPORTED_DEFAULT;
}
+int cls_get_snapset_seq(cls_method_context_t hctx, uint64_t *snap_seq) {
+ librados::TestClassHandler::MethodContext *ctx =
+ reinterpret_cast<librados::TestClassHandler::MethodContext*>(hctx);
+ librados::snap_set_t snapset;
+ int r = ctx->io_ctx_impl->list_snaps(ctx->oid, &snapset);
+ if (r < 0) {
+ return r;
+ }
+
+ *snap_seq = snapset.seq;
+ return 0;
+}
+
int cls_log(int level, const char *format, ...) {
int size = 256;
va_list ap;