]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
test: new mock test cases for librbd image refresh state machine 7299/head
authorJason Dillaman <dillaman@redhat.com>
Wed, 20 Jan 2016 19:00:58 +0000 (14:00 -0500)
committerJason Dillaman <dillaman@redhat.com>
Wed, 20 Jan 2016 19:00:58 +0000 (14:00 -0500)
Specifically tests the ability to dynamically enable/disable RBD
features.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/test/Makefile-client.am
src/test/librbd/image/test_mock_RefreshRequest.cc [new file with mode: 0644]
src/test/librbd/mock/MockExclusiveLock.h
src/test/librbd/mock/MockImageCtx.h
src/test/librbd/test_support.h

index 967dde46baac337ffee4fb4c84e66ee45037083c..0d1225b958ffac76c67331bf70aefbbe7c20e065 100644 (file)
@@ -365,6 +365,7 @@ unittest_librbd_SOURCES = \
        test/librbd/test_mock_Journal.cc \
        test/librbd/exclusive_lock/test_mock_AcquireRequest.cc \
        test/librbd/exclusive_lock/test_mock_ReleaseRequest.cc \
+       test/librbd/image/test_mock_RefreshRequest.cc \
        test/librbd/journal/test_mock_Replay.cc \
        test/librbd/object_map/test_mock_InvalidateRequest.cc \
        test/librbd/object_map/test_mock_LockRequest.cc \
diff --git a/src/test/librbd/image/test_mock_RefreshRequest.cc b/src/test/librbd/image/test_mock_RefreshRequest.cc
new file mode 100644 (file)
index 0000000..6d8791f
--- /dev/null
@@ -0,0 +1,711 @@
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librbd/mock/MockJournal.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/internal.h"
+#include "librbd/Operations.h"
+#include "librbd/image/RefreshRequest.h"
+#include "librbd/image/RefreshParentRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <arpa/inet.h>
+#include <list>
+#include <boost/scope_exit.hpp>
+
+namespace librbd {
+namespace image {
+
+template <>
+struct RefreshParentRequest<MockImageCtx> {
+  static RefreshParentRequest* s_instance;
+  static RefreshParentRequest* create(MockImageCtx &mock_image_ctx,
+                                      const parent_info& parent_md,
+                                      Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+  static bool is_refresh_required(MockImageCtx &mock_image_ctx,
+                                  const parent_info& parent_md) {
+    assert(s_instance != nullptr);
+    return s_instance->is_refresh_required();
+  }
+
+  Context *on_finish = nullptr;
+
+  RefreshParentRequest() {
+    s_instance = this;
+  }
+
+  MOCK_CONST_METHOD0(is_refresh_required, bool());
+  MOCK_METHOD0(send, void());
+  MOCK_METHOD0(apply, void());
+  MOCK_METHOD1(finalize, void(Context *));
+};
+
+RefreshParentRequest<MockImageCtx>* RefreshParentRequest<MockImageCtx>::s_instance = nullptr;
+
+} // namespace image
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/RefreshRequest.cc"
+template class librbd::image::RefreshRequest<librbd::MockImageCtx>;
+
+ACTION_P(TestFeatures, image_ctx) {
+  return ((image_ctx->features & arg0) != 0);
+}
+
+ACTION_P(ShutDownExclusiveLock, image_ctx) {
+  // shutting down exclusive lock will close object map and journal
+  image_ctx->object_map = nullptr;
+  image_ctx->journal = nullptr;
+}
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Return;
+
+class TestMockImageRefreshRequest : public TestMockFixture {
+public:
+  typedef RefreshRequest<MockImageCtx> MockRefreshRequest;
+  typedef RefreshParentRequest<MockImageCtx> MockRefreshParentRequest;
+
+  void expect_v1_read_header(MockImageCtx &mock_image_ctx, int r) {
+    auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                               read(mock_image_ctx.header_oid, _, _, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_v1_get_snapshots(MockImageCtx &mock_image_ctx, int r) {
+    auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                               exec(mock_image_ctx.header_oid, _, "rbd", "snap_list", _, _, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_v1_get_locks(MockImageCtx &mock_image_ctx, int r) {
+    auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                               exec(mock_image_ctx.header_oid, _, "lock", "get_info", _, _, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_get_mutable_metadata(MockImageCtx &mock_image_ctx, int r) {
+    auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                               exec(mock_image_ctx.header_oid, _, "rbd", "get_size", _, _, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+      EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                  exec(mock_image_ctx.header_oid, _, "rbd", "get_features", _, _, _))
+                    .WillOnce(DoDefault());
+      EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                  exec(mock_image_ctx.header_oid, _, "rbd", "get_snapcontext", _, _, _))
+                    .WillOnce(DoDefault());
+      EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                  exec(mock_image_ctx.header_oid, _, "rbd", "get_parent", _, _, _))
+                    .WillOnce(DoDefault());
+      EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                  exec(mock_image_ctx.header_oid, _, "lock", "get_info", _, _, _))
+                    .WillOnce(DoDefault());
+    }
+  }
+
+  void expect_get_flags(MockImageCtx &mock_image_ctx, int r) {
+    auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                               exec(mock_image_ctx.header_oid, _, "rbd", "get_flags", _, _, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_get_snapshots(MockImageCtx &mock_image_ctx, int r) {
+    auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                               exec(mock_image_ctx.header_oid, _, "rbd", "get_snapshot_name", _, _, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+      EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                  exec(mock_image_ctx.header_oid, _, "rbd", "get_size", _, _, _))
+                    .WillOnce(DoDefault());
+      EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                  exec(mock_image_ctx.header_oid, _, "rbd", "get_parent", _, _, _))
+                    .WillOnce(DoDefault());
+      EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                  exec(mock_image_ctx.header_oid, _, "rbd", "get_protection_status", _, _, _))
+                    .WillOnce(DoDefault());
+    }
+  }
+
+  void expect_add_snap(MockImageCtx &mock_image_ctx,
+                       const std::string &snap_name, uint64_t snap_id) {
+    EXPECT_CALL(mock_image_ctx, add_snap(snap_name, snap_id, _, _, _, _));
+  }
+
+  void expect_init_exclusive_lock(MockImageCtx &mock_image_ctx,
+                                  MockExclusiveLock &mock_exclusive_lock,
+                                  int r) {
+    EXPECT_CALL(mock_image_ctx, create_exclusive_lock())
+                  .WillOnce(Return(&mock_exclusive_lock));
+    EXPECT_CALL(mock_exclusive_lock, init(_))
+                  .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_shut_down_exclusive_lock(MockImageCtx &mock_image_ctx,
+                                       MockExclusiveLock &mock_exclusive_lock,
+                                       int r) {
+    EXPECT_CALL(mock_exclusive_lock, shut_down(_))
+                  .WillOnce(DoAll(ShutDownExclusiveLock(&mock_image_ctx),
+                                  CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+  }
+
+  void expect_init_layout(MockImageCtx &mock_image_ctx) {
+    EXPECT_CALL(mock_image_ctx, init_layout());
+  }
+
+  void expect_test_features(MockImageCtx &mock_image_ctx) {
+    EXPECT_CALL(mock_image_ctx, test_features(_, _))
+                  .WillRepeatedly(TestFeatures(&mock_image_ctx));
+  }
+
+  void expect_refresh_parent_is_required(MockRefreshParentRequest &mock_refresh_parent_request,
+                                         bool required) {
+    EXPECT_CALL(mock_refresh_parent_request, is_refresh_required())
+                  .WillRepeatedly(Return(required));
+  }
+
+  void expect_refresh_parent_send(MockImageCtx &mock_image_ctx,
+                                  MockRefreshParentRequest &mock_refresh_parent_request,
+                                  int r) {
+    EXPECT_CALL(mock_refresh_parent_request, send())
+                  .WillOnce(FinishRequest(&mock_refresh_parent_request, r,
+                                          &mock_image_ctx));
+  }
+
+  void expect_refresh_parent_apply(MockRefreshParentRequest &mock_refresh_parent_request) {
+    EXPECT_CALL(mock_refresh_parent_request, apply());
+  }
+
+  void expect_refresh_parent_finalize(MockImageCtx &mock_image_ctx,
+                                      MockRefreshParentRequest &mock_refresh_parent_request,
+                                      int r) {
+    EXPECT_CALL(mock_refresh_parent_request, finalize(_))
+                  .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_is_exclusive_lock_owner(MockExclusiveLock &mock_exclusive_lock,
+                                      bool is_owner) {
+    EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillOnce(Return(is_owner));
+  }
+
+  void expect_open_journal(MockImageCtx &mock_image_ctx,
+                           MockJournal &mock_journal, int r) {
+    EXPECT_CALL(mock_image_ctx, create_journal())
+                  .WillOnce(Return(&mock_journal));
+    EXPECT_CALL(mock_journal, open(_))
+                  .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_close_journal(MockImageCtx &mock_image_ctx,
+                            MockJournal &mock_journal, int r) {
+    EXPECT_CALL(mock_journal, close(_))
+                  .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_open_object_map(MockImageCtx &mock_image_ctx,
+                              MockObjectMap &mock_object_map, int r) {
+    EXPECT_CALL(mock_image_ctx, create_object_map(_))
+                  .WillOnce(Return(&mock_object_map));
+    EXPECT_CALL(mock_object_map, open(_))
+                  .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_close_object_map(MockImageCtx &mock_image_ctx,
+                               MockObjectMap &mock_object_map, int r) {
+    EXPECT_CALL(mock_object_map, close(_))
+                  .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_get_snap_id(MockImageCtx &mock_image_ctx,
+                          const std::string &snap_name, uint64_t snap_id) {
+    EXPECT_CALL(mock_image_ctx, get_snap_id(snap_name)).WillOnce(Return(snap_id));
+  }
+};
+
+TEST_F(TestMockImageRefreshRequest, SuccessV1) {
+  REQUIRE_FORMAT_V1();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  expect_op_work_queue(mock_image_ctx);
+  expect_test_features(mock_image_ctx);
+
+  InSequence seq;
+  expect_v1_read_header(mock_image_ctx, 0);
+  expect_v1_get_snapshots(mock_image_ctx, 0);
+  expect_v1_get_locks(mock_image_ctx, 0);
+  expect_init_layout(mock_image_ctx);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessSnapshotV1) {
+  REQUIRE_FORMAT_V1();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  expect_op_work_queue(mock_image_ctx);
+  expect_test_features(mock_image_ctx);
+
+  InSequence seq;
+  expect_v1_read_header(mock_image_ctx, 0);
+  expect_v1_get_snapshots(mock_image_ctx, 0);
+  expect_v1_get_locks(mock_image_ctx, 0);
+  expect_init_layout(mock_image_ctx);
+  expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessV2) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx 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_flags(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, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessSnapshotV2) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, snap_create(*ictx, "snap"));
+
+  MockImageCtx 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_flags(mock_image_ctx, 0);
+  expect_get_flags(mock_image_ctx, 0);
+  expect_get_snapshots(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);
+  }
+  expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessSetSnapshotV2) {
+  REQUIRE_FORMAT_V2();
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, snap_create(*ictx, "snap"));
+  ASSERT_EQ(0, librbd::snap_set(ictx, "snap"));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+  MockObjectMap mock_object_map;
+  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_flags(mock_image_ctx, 0);
+  expect_get_flags(mock_image_ctx, 0);
+  expect_get_snapshots(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+  }
+  expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+  expect_get_snap_id(mock_image_ctx, "snap", ictx->snap_ids.begin()->second);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, SuccessChild) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  librbd::ImageCtx *ictx;
+  librbd::ImageCtx *ictx2 = nullptr;
+  std::string clone_name = get_temp_image_name();
+
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+  ASSERT_EQ(0, snap_create(*ictx, "snap"));
+  ASSERT_EQ(0, snap_protect(*ictx, "snap"));
+  BOOST_SCOPE_EXIT_ALL((&)) {
+    if (ictx2 != nullptr) {
+      close_image(ictx2);
+    }
+
+    librbd::NoOpProgressContext no_op;
+    ASSERT_EQ(0, librbd::remove(m_ioctx, clone_name.c_str(), no_op));
+    ASSERT_EQ(0, ictx->operations->snap_unprotect("snap"));
+  };
+
+  int order = ictx->order;
+  ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
+                             clone_name.c_str(), ictx->features, &order, 0, 0));
+
+  ASSERT_EQ(0, open_image(clone_name, &ictx2));
+
+  MockImageCtx mock_image_ctx(*ictx2);
+  MockRefreshParentRequest *mock_refresh_parent_request = new MockRefreshParentRequest();
+  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_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(*mock_refresh_parent_request, true);
+  expect_refresh_parent_send(mock_image_ctx, *mock_refresh_parent_request, 0);
+  if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+    expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0);
+  }
+  expect_refresh_parent_apply(*mock_refresh_parent_request);
+  expect_refresh_parent_finalize(mock_image_ctx, *mock_refresh_parent_request, 0);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, DisableExclusiveLock) {
+  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+
+  MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock();
+  mock_image_ctx.exclusive_lock = mock_exclusive_lock;
+
+  MockObjectMap mock_object_map;
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    mock_image_ctx.object_map = &mock_object_map;
+  }
+
+  MockJournal mock_journal;
+  if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+    mock_image_ctx.journal = &mock_journal;
+  }
+
+  ASSERT_EQ(0, update_features(ictx,
+                               RBD_FEATURE_EXCLUSIVE_LOCK |
+                               RBD_FEATURE_OBJECT_MAP |
+                               RBD_FEATURE_FAST_DIFF |
+                               RBD_FEATURE_JOURNALING, false));
+
+  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_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+  expect_shut_down_exclusive_lock(mock_image_ctx, *mock_exclusive_lock, 0);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableJournalWithExclusiveLock) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  ASSERT_EQ(0, update_features(ictx,
+                               RBD_FEATURE_OBJECT_MAP |
+                               RBD_FEATURE_FAST_DIFF, false));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+
+  MockExclusiveLock mock_exclusive_lock;
+  mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+  MockJournal mock_journal;
+
+  expect_op_work_queue(mock_image_ctx);
+  expect_test_features(mock_image_ctx);
+  expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
+
+  // journal should be immediately opened if exclusive lock owned
+  InSequence seq;
+  expect_get_mutable_metadata(mock_image_ctx, 0);
+  expect_get_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+  expect_open_journal(mock_image_ctx, mock_journal, 0);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableJournalWithoutExclusiveLock) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  ASSERT_EQ(0, update_features(ictx,
+                               RBD_FEATURE_OBJECT_MAP |
+                               RBD_FEATURE_FAST_DIFF, false));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+
+  MockExclusiveLock mock_exclusive_lock;
+  mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+  expect_op_work_queue(mock_image_ctx);
+  expect_test_features(mock_image_ctx);
+  expect_is_exclusive_lock_owner(mock_exclusive_lock, false);
+
+  // do not open the journal if exclusive lock is not owned
+  InSequence seq;
+  expect_get_mutable_metadata(mock_image_ctx, 0);
+  expect_get_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, DisableJournal) {
+  REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+
+  MockExclusiveLock mock_exclusive_lock;
+  mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+  MockObjectMap mock_object_map;
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    mock_image_ctx.object_map = &mock_object_map;
+  }
+
+  MockJournal *mock_journal = new MockJournal();
+  mock_image_ctx.journal = mock_journal;
+
+  ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false));
+
+  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_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+  expect_close_journal(mock_image_ctx, *mock_journal, 0);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithExclusiveLock) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+
+  MockExclusiveLock mock_exclusive_lock;
+  mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+  MockObjectMap mock_object_map;
+
+  expect_op_work_queue(mock_image_ctx);
+  expect_test_features(mock_image_ctx);
+  expect_is_exclusive_lock_owner(mock_exclusive_lock, true);
+
+  // object map should be immediately opened if exclusive lock owned
+  InSequence seq;
+  expect_get_mutable_metadata(mock_image_ctx, 0);
+  expect_get_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+  expect_open_object_map(mock_image_ctx, mock_object_map, 0);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithoutExclusiveLock) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+
+  MockExclusiveLock mock_exclusive_lock;
+  mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+  expect_op_work_queue(mock_image_ctx);
+  expect_test_features(mock_image_ctx);
+  expect_is_exclusive_lock_owner(mock_exclusive_lock, 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_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageRefreshRequest, DisableObjectMap) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockRefreshParentRequest mock_refresh_parent_request;
+
+  MockExclusiveLock mock_exclusive_lock;
+  mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+
+  MockObjectMap *mock_object_map = new MockObjectMap();
+  mock_image_ctx.object_map = mock_object_map;
+
+  MockJournal mock_journal;
+  if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+    mock_image_ctx.journal = &mock_journal;
+  }
+
+  ASSERT_EQ(0, update_features(ictx,
+                               RBD_FEATURE_OBJECT_MAP |
+                               RBD_FEATURE_FAST_DIFF, false));
+
+  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_flags(mock_image_ctx, 0);
+  expect_refresh_parent_is_required(mock_refresh_parent_request, false);
+  expect_close_object_map(mock_image_ctx, *mock_object_map, 0);
+
+  C_SaferCond ctx;
+  MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx);
+  req->send();
+
+  ASSERT_EQ(0, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
index 8227d3edb190447dbf1e691bfc807e61881a9a00..430dbc7e1b4662902de6fcc8a5813aec6fc95e62 100644 (file)
@@ -17,6 +17,7 @@ struct MockExclusiveLock {
 
   MOCK_METHOD1(assert_header_locked, void(librados::ObjectWriteOperation *));
 
+  MOCK_METHOD1(init, void(Context*));
   MOCK_METHOD1(shut_down, void(Context*));
 };
 
index 7d198f5caba3267f3f404376f18965f9a9ad4dfa..0c9b7a1a4a6fa3921fea259e380aef5674af1e98 100644 (file)
@@ -27,21 +27,31 @@ struct MockImageCtx {
   MockImageCtx(librbd::ImageCtx &image_ctx)
     : image_ctx(&image_ctx),
       cct(image_ctx.cct),
+      snap_name(image_ctx.snap_name),
       snap_id(image_ctx.snap_id),
+      snap_exists(image_ctx.snap_exists),
       snapc(image_ctx.snapc),
       snaps(image_ctx.snaps),
       snap_info(image_ctx.snap_info),
+      snap_ids(image_ctx.snap_ids),
       object_cacher(image_ctx.object_cacher),
       old_format(image_ctx.old_format),
       read_only(image_ctx.read_only),
+      lockers(image_ctx.lockers),
+      exclusive_locked(image_ctx.exclusive_locked),
+      lock_tag(image_ctx.lock_tag),
       owner_lock("owner_lock"),
       md_lock("md_lock"),
+      cache_lock("cache_lock"),
       snap_lock("snap_lock"),
       parent_lock("parent_lock"),
       object_map_lock("object_map_lock"),
       async_ops_lock("async_ops_lock"),
+      order(image_ctx.order),
       size(image_ctx.size),
       features(image_ctx.features),
+      flags(image_ctx.flags),
+      object_prefix(image_ctx.object_prefix),
       header_oid(image_ctx.header_oid),
       id(image_ctx.id),
       parent_md(image_ctx.parent_md),
@@ -95,6 +105,8 @@ struct MockImageCtx {
     ctx.wait();
   }
 
+  MOCK_METHOD0(init_layout, void());
+
   MOCK_CONST_METHOD1(get_object_name, std::string(uint64_t));
   MOCK_CONST_METHOD0(get_current_size, uint64_t());
   MOCK_CONST_METHOD1(get_image_size, uint64_t(librados::snap_t));
@@ -120,37 +132,53 @@ struct MockImageCtx {
   MOCK_METHOD1(shut_down_cache, void(Context *));
 
   MOCK_CONST_METHOD1(test_features, bool(uint64_t test_features));
+  MOCK_CONST_METHOD2(test_features, bool(uint64_t test_features,
+                                         const RWLock &in_snap_lock));
 
   MOCK_METHOD1(cancel_async_requests, void(Context*));
 
+  MOCK_METHOD0(create_exclusive_lock, MockExclusiveLock*());
   MOCK_METHOD1(create_object_map, MockObjectMap*(uint64_t));
   MOCK_METHOD0(create_journal, MockJournal*());
 
   ImageCtx *image_ctx;
   CephContext *cct;
 
+  std::string snap_name;
   uint64_t snap_id;
+  bool snap_exists;
+
   ::SnapContext snapc;
   std::vector<librados::snap_t> snaps;
   std::map<librados::snap_t, SnapInfo> snap_info;
+  std::map<std::string, librados::snap_t> snap_ids;
 
   ObjectCacher *object_cacher;
 
   bool old_format;
   bool read_only;
 
+  std::map<rados::cls::lock::locker_id_t,
+           rados::cls::lock::locker_info_t> lockers;
+  bool exclusive_locked;
+  std::string lock_tag;
+
   librados::IoCtx md_ctx;
   librados::IoCtx data_ctx;
 
   RWLock owner_lock;
   RWLock md_lock;
+  Mutex cache_lock;
   RWLock snap_lock;
   RWLock parent_lock;
   RWLock object_map_lock;
   Mutex async_ops_lock;
 
+  uint8_t order;
   uint64_t size;
   uint64_t features;
+  uint64_t flags;
+  std::string object_prefix;
   std::string header_oid;
   std::string id;
   parent_info parent_md;
index 5bdd958573d276993db6036f442cda1422b595b5..63c5e3a3455817b4367ad2624833992235bc40c7 100644 (file)
@@ -17,3 +17,11 @@ int create_image_pp(librbd::RBD &rbd, librados::IoCtx &ioctx,
   }                                      \
 }
 
+#define REQUIRE_FORMAT_V1() {            \
+  if (is_feature_enabled(0)) {                   \
+    std::cout << "SKIPPING" << std::endl; \
+    return SUCCEED();                    \
+  }                                      \
+}
+
+#define REQUIRE_FORMAT_V2() REQUIRE_FEATURE(0)