From 57ef57abd971a5aeb38dc0f57e5863ff43c8094d Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Sun, 15 Jul 2018 12:21:08 +0300 Subject: [PATCH] cls/rbd: add assert_snapc_seq method Signed-off-by: Mykola Golub --- src/cls/rbd/cls_rbd.cc | 45 ++++++++++++++ src/cls/rbd/cls_rbd_client.cc | 17 ++++++ src/cls/rbd/cls_rbd_client.h | 7 +++ src/cls/rbd/cls_rbd_types.cc | 15 +++++ src/cls/rbd/cls_rbd_types.h | 19 ++++++ src/objclass/class_api.cc | 9 +++ src/objclass/objclass.h | 2 + src/test/cls_rbd/test_cls_rbd.cc | 59 +++++++++++++++++++ .../librados_test_stub/LibradosTestStub.cc | 13 ++++ 9 files changed, 186 insertions(+) diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index 49855bc3552..bfc1ea3154b 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -3735,6 +3735,46 @@ int migration_remove(cls_method_context_t hctx, bufferlist *in, 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) @@ -6661,6 +6701,7 @@ CLS_INIT(rbd) 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; @@ -6838,6 +6879,10 @@ CLS_INIT(rbd) 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", diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc index 98e9cea9967..087ed597b23 100644 --- a/src/cls/rbd/cls_rbd_client.cc +++ b/src/cls/rbd/cls_rbd_client.cc @@ -1667,6 +1667,23 @@ namespace librbd { 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); diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h index c96900e9864..2acc5be4743 100644 --- a/src/cls/rbd/cls_rbd_client.h +++ b/src/cls/rbd/cls_rbd_client.h @@ -276,6 +276,13 @@ namespace librbd { 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); diff --git a/src/cls/rbd/cls_rbd_types.cc b/src/cls/rbd/cls_rbd_types.cc index de90bcfcde4..a4613c2d1ee 100644 --- a/src/cls/rbd/cls_rbd_types.cc +++ b/src/cls/rbd/cls_rbd_types.cc @@ -842,5 +842,20 @@ std::ostream& operator<<(std::ostream& os, 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(state) << ")"; + break; + } + return os; +} + } // namespace rbd } // namespace cls diff --git a/src/cls/rbd/cls_rbd_types.h b/src/cls/rbd/cls_rbd_types.h index 8b866bac06d..cd8b01038bf 100644 --- a/src/cls/rbd/cls_rbd_types.h +++ b/src/cls/rbd/cls_rbd_types.h @@ -691,6 +691,25 @@ std::ostream& operator<<(std::ostream& os, const MigrationSpec& migration_spec); 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(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(int_state); +} + +std::ostream& operator<<(std::ostream& os, const AssertSnapcSeqState& state); + } // namespace rbd } // namespace cls diff --git a/src/objclass/class_api.cc b/src/objclass/class_api.cc index fd45c813f5b..3edfbf6244c 100644 --- a/src/objclass/class_api.cc +++ b/src/objclass/class_api.cc @@ -705,6 +705,15 @@ void cls_cxx_subop_version(cls_method_context_t hctx, string *s) *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; diff --git a/src/objclass/objclass.h b/src/objclass/objclass.h index d36867de548..df2017d9f5d 100644 --- a/src/objclass/objclass.h +++ b/src/objclass/objclass.h @@ -160,6 +160,8 @@ extern uint64_t cls_get_client_features(cls_method_context_t hctx); /* 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' diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index f8a386d5162..30e0c2881e6 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -2879,3 +2879,62 @@ TEST_F(TestClsRbd, migration_v1) 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 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)); +} diff --git a/src/test/librados_test_stub/LibradosTestStub.cc b/src/test/librados_test_stub/LibradosTestStub.cc index 49bc1be6795..87b17fda82f 100644 --- a/src/test/librados_test_stub/LibradosTestStub.cc +++ b/src/test/librados_test_stub/LibradosTestStub.cc @@ -1355,6 +1355,19 @@ uint64_t cls_get_client_features(cls_method_context_t hctx) { 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(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; -- 2.39.5