librbd: retrieve the op features on image refresh

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
This commit is contained in:
Jason Dillaman 2018-01-05 13:54:24 -05:00
parent 209f6fc5a4
commit fef89753d8
5 changed files with 155 additions and 18 deletions

View File

@ -129,6 +129,8 @@ namespace librbd {
cls::rbd::GroupSpec group_spec;
uint64_t stripe_unit, stripe_count;
uint64_t flags;
uint64_t op_features = 0;
bool operations_disabled = false;
utime_t create_timestamp;
file_layout_t layout;

View File

@ -403,6 +403,49 @@ Context *RefreshRequest<I>::handle_v2_get_flags(int *result) {
return m_on_finish;
}
send_v2_get_op_features();
return nullptr;
}
template <typename I>
void RefreshRequest<I>::send_v2_get_op_features() {
if ((m_features & RBD_FEATURE_OPERATIONS) == 0LL) {
send_v2_get_group();
return;
}
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << this << " " << __func__ << dendl;
librados::ObjectReadOperation op;
cls_client::op_features_get_start(&op);
librados::AioCompletion *comp = create_rados_callback<
RefreshRequest<I>, &RefreshRequest<I>::handle_v2_get_op_features>(this);
m_out_bl.clear();
int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
&m_out_bl);
assert(r == 0);
comp->release();
}
template <typename I>
Context *RefreshRequest<I>::handle_v2_get_op_features(int *result) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << this << " " << __func__ << ": "
<< "r=" << *result << dendl;
// -EOPNOTSUPP handler not required since feature bit implies OSD
// supports the method
if (*result == 0) {
bufferlist::iterator it = m_out_bl.begin();
cls_client::op_features_get_finish(&it, &m_op_features);
} else if (*result < 0) {
lderr(cct) << "failed to retrieve op features: " << cpp_strerror(*result)
<< dendl;
return m_on_finish;
}
send_v2_get_group();
return nullptr;
}
@ -1079,11 +1122,16 @@ void RefreshRequest<I>::apply() {
m_image_ctx.order = m_order;
m_image_ctx.features = 0;
m_image_ctx.flags = 0;
m_image_ctx.op_features = 0;
m_image_ctx.operations_disabled = false;
m_image_ctx.object_prefix = std::move(m_object_prefix);
m_image_ctx.init_layout();
} else {
m_image_ctx.features = m_features;
m_image_ctx.flags = m_flags;
m_image_ctx.op_features = m_op_features;
m_image_ctx.operations_disabled = (
(m_op_features & ~RBD_OPERATION_FEATURES_ALL) != 0ULL);
m_image_ctx.group_spec = m_group_spec;
m_image_ctx.parent_md = m_parent_md;
}

View File

@ -57,6 +57,9 @@ private:
* v |
* V2_GET_FLAGS |
* | |
* v (skip if not enabled) |
* V2_GET_OP_FEATURES |
* | |
* v |
* V2_GET_GROUP |
* | |
@ -130,6 +133,7 @@ private:
uint64_t m_features = 0;
uint64_t m_incompatible_features = 0;
uint64_t m_flags = 0;
uint64_t m_op_features = 0;
std::string m_last_metadata_key;
std::map<std::string, bufferlist> m_metadata;
@ -176,6 +180,9 @@ private:
void send_v2_get_flags();
Context *handle_v2_get_flags(int *result);
void send_v2_get_op_features();
Context *handle_v2_get_op_features(int *result);
void send_v2_get_group();
Context *handle_v2_get_group(int *result);

View File

@ -89,6 +89,7 @@ using ::testing::_;
using ::testing::DoAll;
using ::testing::DoDefault;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::WithArg;
using ::testing::StrEq;
@ -134,16 +135,25 @@ public:
}
}
void expect_get_mutable_metadata(MockRefreshImageCtx &mock_image_ctx, int r) {
void expect_get_mutable_metadata(MockRefreshImageCtx &mock_image_ctx,
uint64_t features, int r) {
auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("get_size"), _, _, _));
if (r < 0) {
expect.WillOnce(Return(r));
} else {
uint64_t incompatible = (
mock_image_ctx.read_only ? features & RBD_FEATURES_INCOMPATIBLE :
features & RBD_FEATURES_RW_INCOMPATIBLE);
expect.WillOnce(DoDefault());
EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("get_features"), _, _, _))
.WillOnce(DoDefault());
.WillOnce(WithArg<5>(Invoke([features, incompatible](bufferlist* out_bl) {
encode(features, *out_bl);
encode(incompatible, *out_bl);
return 0;
})));
EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("get_snapcontext"), _, _, _))
.WillOnce(DoDefault());
@ -178,6 +188,17 @@ public:
}
}
void expect_get_op_features(MockRefreshImageCtx &mock_image_ctx,
uint64_t op_features, int r) {
EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
StrEq("op_features_get"), _, _, _))
.WillOnce(WithArg<5>(Invoke([op_features, r](bufferlist* out_bl) {
encode(op_features, *out_bl);
return r;
})));
}
void expect_get_group(MockRefreshImageCtx &mock_image_ctx, int r) {
auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
@ -416,7 +437,7 @@ TEST_F(TestMockImageRefreshRequest, SuccessV2) {
expect_test_features(mock_image_ctx);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -447,7 +468,7 @@ TEST_F(TestMockImageRefreshRequest, SuccessSnapshotV2) {
expect_test_features(mock_image_ctx);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -484,7 +505,7 @@ TEST_F(TestMockImageRefreshRequest, SuccessSetSnapshotV2) {
expect_test_features(mock_image_ctx);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -540,7 +561,7 @@ TEST_F(TestMockImageRefreshRequest, SuccessChild) {
expect_test_features(mock_image_ctx);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -592,7 +613,7 @@ TEST_F(TestMockImageRefreshRequest, SuccessChildDontOpenParent) {
expect_test_features(mock_image_ctx);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -608,6 +629,41 @@ TEST_F(TestMockImageRefreshRequest, SuccessChildDontOpenParent) {
ASSERT_EQ(0, ctx.wait());
}
TEST_F(TestMockImageRefreshRequest, SuccessOpFeatures) {
REQUIRE_FORMAT_V2();
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
MockRefreshImageCtx mock_image_ctx(*ictx);
MockRefreshParentRequest mock_refresh_parent_request;
MockExclusiveLock mock_exclusive_lock;
expect_op_work_queue(mock_image_ctx);
expect_test_features(mock_image_ctx);
mock_image_ctx.features |= RBD_FEATURE_OPERATIONS;
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, mock_image_ctx.features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_op_features(mock_image_ctx, 4096, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
}
C_SaferCond ctx;
MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, false, false, &ctx);
req->send();
ASSERT_EQ(0, ctx.wait());
ASSERT_EQ(4096, mock_image_ctx.op_features);
ASSERT_TRUE(mock_image_ctx.operations_disabled);
}
TEST_F(TestMockImageRefreshRequest, DisableExclusiveLock) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
@ -650,13 +706,15 @@ TEST_F(TestMockImageRefreshRequest, DisableExclusiveLock) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
expect_op_work_queue(mock_image_ctx);
expect_test_features(mock_image_ctx);
// verify that exclusive lock is properly handled when object map
// and journaling were never enabled (or active)
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -703,13 +761,15 @@ TEST_F(TestMockImageRefreshRequest, DisableExclusiveLockWhileAcquiringLock) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
expect_op_work_queue(mock_image_ctx);
expect_test_features(mock_image_ctx);
// verify that exclusive lock is properly handled when object map
// and journaling were never enabled (or active)
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -739,6 +799,8 @@ TEST_F(TestMockImageRefreshRequest, JournalDisabledByPolicy) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
MockRefreshImageCtx mock_image_ctx(*ictx);
MockRefreshParentRequest mock_refresh_parent_request;
@ -752,7 +814,7 @@ TEST_F(TestMockImageRefreshRequest, JournalDisabledByPolicy) {
expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -786,6 +848,8 @@ TEST_F(TestMockImageRefreshRequest, EnableJournalWithExclusiveLock) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
MockRefreshImageCtx mock_image_ctx(*ictx);
MockRefreshParentRequest mock_refresh_parent_request;
@ -800,7 +864,7 @@ TEST_F(TestMockImageRefreshRequest, EnableJournalWithExclusiveLock) {
// journal should be immediately opened if exclusive lock owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -835,6 +899,8 @@ TEST_F(TestMockImageRefreshRequest, EnableJournalWithoutExclusiveLock) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
MockRefreshImageCtx mock_image_ctx(*ictx);
MockRefreshParentRequest mock_refresh_parent_request;
@ -847,7 +913,7 @@ TEST_F(TestMockImageRefreshRequest, EnableJournalWithoutExclusiveLock) {
// do not open the journal if exclusive lock is not owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -887,12 +953,14 @@ TEST_F(TestMockImageRefreshRequest, DisableJournal) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
expect_op_work_queue(mock_image_ctx);
expect_test_features(mock_image_ctx);
// verify journal is closed if feature disabled
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -923,6 +991,8 @@ TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithExclusiveLock) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
MockRefreshImageCtx mock_image_ctx(*ictx);
MockRefreshParentRequest mock_refresh_parent_request;
@ -937,7 +1007,7 @@ TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithExclusiveLock) {
// object map should be immediately opened if exclusive lock owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -963,6 +1033,8 @@ TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithoutExclusiveLock) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
MockRefreshImageCtx mock_image_ctx(*ictx);
MockRefreshParentRequest mock_refresh_parent_request;
@ -975,7 +1047,7 @@ TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithoutExclusiveLock) {
// do not open the object map if exclusive lock is not owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -1019,12 +1091,14 @@ TEST_F(TestMockImageRefreshRequest, DisableObjectMap) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
expect_op_work_queue(mock_image_ctx);
expect_test_features(mock_image_ctx);
// verify object map is closed if feature disabled
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -1050,6 +1124,8 @@ TEST_F(TestMockImageRefreshRequest, OpenObjectMapError) {
false));
}
ASSERT_EQ(0, ictx->state->refresh());
MockRefreshImageCtx mock_image_ctx(*ictx);
MockRefreshParentRequest mock_refresh_parent_request;
@ -1064,7 +1140,7 @@ TEST_F(TestMockImageRefreshRequest, OpenObjectMapError) {
// object map should be immediately opened if exclusive lock owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
@ -1093,7 +1169,7 @@ TEST_F(TestMockImageRefreshRequest, ApplyMetadataError) {
expect_test_features(mock_image_ctx);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
expect_get_mutable_metadata(mock_image_ctx, ictx->features, 0);
expect_get_metadata(mock_image_ctx, 0);
expect_apply_metadata(mock_image_ctx, -EINVAL);
expect_get_flags(mock_image_ctx, 0);

View File

@ -71,6 +71,8 @@ struct MockImageCtx {
size(image_ctx.size),
features(image_ctx.features),
flags(image_ctx.flags),
op_features(image_ctx.op_features),
operations_disabled(image_ctx.operations_disabled),
stripe_unit(image_ctx.stripe_unit),
stripe_count(image_ctx.stripe_count),
object_prefix(image_ctx.object_prefix),
@ -262,6 +264,8 @@ struct MockImageCtx {
uint64_t size;
uint64_t features;
uint64_t flags;
uint64_t op_features;
bool operations_disabled;
uint64_t stripe_unit;
uint64_t stripe_count;
std::string object_prefix;