// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
+#include <boost/algorithm/string/predicate.hpp>
+#include "include/assert.h"
+
#include "librbd/image/RefreshRequest.h"
#include "common/dout.h"
#include "common/errno.h"
namespace librbd {
namespace image {
+namespace {
+
+const uint64_t MAX_METADATA_ITEMS = 128;
+
+}
+
using util::create_rados_callback;
using util::create_async_context_callback;
using util::create_context_callback;
m_incomplete_update = true;
}
+ send_v2_get_metadata();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_metadata() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": "
+ << "start_key=" << m_last_metadata_key << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::metadata_list_start(&op, m_last_metadata_key, MAX_METADATA_ITEMS);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp =
+ create_rados_callback<klass, &klass::handle_v2_get_metadata>(this);
+ m_out_bl.clear();
+ m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_metadata(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
+
+ std::map<std::string, bufferlist> metadata;
+ if (*result == 0) {
+ bufferlist::iterator it = m_out_bl.begin();
+ *result = cls_client::metadata_list_finish(&it, &metadata);
+ }
+
+ if (*result == -EOPNOTSUPP || *result == -EIO) {
+ ldout(cct, 10) << "config metadata not supported by OSD" << dendl;
+ } else if (*result < 0) {
+ lderr(cct) << "failed to retrieve metadata: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
+ if (!metadata.empty()) {
+ m_metadata.insert(metadata.begin(), metadata.end());
+ m_last_metadata_key = metadata.rbegin()->first;
+ if (boost::starts_with(m_last_metadata_key,
+ ImageCtx::METADATA_CONF_PREFIX)) {
+ send_v2_get_metadata();
+ return nullptr;
+ }
+ }
+
+ m_image_ctx.apply_metadata(m_metadata);
+
send_v2_get_flags();
return nullptr;
}
}
}
+ void expect_get_metadata(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"), StrEq("metadata_list"), _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
void expect_get_flags(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"), StrEq("get_flags"), _, _, _));
}
}
+ void expect_apply_metadata(MockRefreshImageCtx &mock_image_ctx,
+ int r) {
+ EXPECT_CALL(mock_image_ctx, apply_metadata(_))
+ .WillOnce(Return(r));
+ }
+
void expect_add_snap(MockRefreshImageCtx &mock_image_ctx,
const std::string &snap_name, uint64_t snap_id) {
EXPECT_CALL(mock_image_ctx, add_snap(_, snap_name, snap_id, _, _, _, _, _));
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(*mock_refresh_parent_request, true);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
// and journaling were never enabled (or active)
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// and journaling were never enabled (or active)
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// journal should be immediately opened if exclusive lock owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// do not open the journal if exclusive lock is not owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// verify journal is closed if feature disabled
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// object map should be immediately opened if exclusive lock owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// do not open the object map if exclusive lock is not owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// verify object map is closed if feature disabled
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
// object map should be immediately opened if exclusive lock owned
InSequence seq;
expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, 0);
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
ASSERT_EQ(nullptr, mock_image_ctx.object_map);
}
+TEST_F(TestMockImageRefreshRequest, ApplyMetadataError) {
+ 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);
+
+ InSequence seq;
+ expect_get_mutable_metadata(mock_image_ctx, 0);
+ expect_get_metadata(mock_image_ctx, 0);
+ expect_apply_metadata(mock_image_ctx, -EINVAL);
+ expect_get_flags(mock_image_ctx, 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());
+}
+
} // namespace image
} // namespace librbd