]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: initial config watcher implementation
authorJason Dillaman <dillaman@redhat.com>
Mon, 27 Jul 2020 19:31:09 +0000 (15:31 -0400)
committerJason Dillaman <dillaman@redhat.com>
Wed, 19 Aug 2020 20:23:52 +0000 (16:23 -0400)
The config watcher will initially observe all "rbd_" configuration
updates received from the MON that have not been locally overridden
at the pool and/or image level.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
(cherry picked from commit 54819a1aa95fe1be8b8ef44f8417e3fe82d9ec89)

Conflicts:
src/test/librbd/mock/MockImageState.h
- once again, I don't see why git flagged this as a conflict

(cherry picked from commit c830365a8a243bd4976a682af1bf8d768924eaff)

Conflicts:
src/librbd/ConfigWatcher.cc: image_lock -> md_lock
src/test/librbd/mock/MockImageState.h: trivial resolution

src/librbd/CMakeLists.txt
src/librbd/ConfigWatcher.cc [new file with mode: 0644]
src/librbd/ConfigWatcher.h [new file with mode: 0644]
src/test/librbd/CMakeLists.txt
src/test/librbd/mock/MockImageCtx.h
src/test/librbd/mock/MockImageState.h
src/test/librbd/test_mock_ConfigWatcher.cc [new file with mode: 0644]

index 19d942a7b8eab046f16077122f7dee0606befb29..30626d02bdf667c7854ac067e6c606f298ed1d82 100644 (file)
@@ -8,6 +8,7 @@ add_library(rbd_types STATIC
 set(librbd_internal_srcs
   AsyncObjectThrottle.cc
   AsyncRequest.cc
+  ConfigWatcher.cc
   DeepCopyRequest.cc
   ExclusiveLock.cc
   ImageCtx.cc
diff --git a/src/librbd/ConfigWatcher.cc b/src/librbd/ConfigWatcher.cc
new file mode 100644 (file)
index 0000000..f8f1701
--- /dev/null
@@ -0,0 +1,116 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/ConfigWatcher.h"
+#include "common/config_obs.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/api/Config.h"
+#include <deque>
+#include <string>
+#include <vector>
+#include <boost/algorithm/string/predicate.hpp>
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::ConfigWatcher: " \
+                           << __func__ << ": "
+
+namespace librbd {
+
+template <typename I>
+struct ConfigWatcher<I>::Observer : public md_config_obs_t {
+  ConfigWatcher<I>* m_config_watcher;
+
+  std::deque<std::string> m_config_key_strs;
+  mutable std::vector<const char*> m_config_keys;
+
+  Observer(CephContext* cct, ConfigWatcher<I>* config_watcher)
+    : m_config_watcher(config_watcher) {
+    const std::string rbd_key_prefix("rbd_");
+    auto& schema = cct->_conf.get_schema();
+    for (auto& pair : schema) {
+      // watch all "rbd_" keys for simplicity
+      if (!boost::starts_with(pair.first, rbd_key_prefix)) {
+        continue;
+      }
+
+      m_config_key_strs.emplace_back(pair.first);
+    }
+
+    m_config_keys.reserve(m_config_key_strs.size());
+    for (auto& key : m_config_key_strs) {
+      m_config_keys.emplace_back(key.c_str());
+    }
+    m_config_keys.emplace_back(nullptr);
+  }
+
+  const char** get_tracked_conf_keys() const override {
+    ceph_assert(!m_config_keys.empty());
+    return &m_config_keys[0];
+  }
+
+  void handle_conf_change(const ConfigProxy& conf,
+                          const std::set <std::string> &changed) override {
+    m_config_watcher->handle_global_config_change(changed);
+  }
+};
+
+template <typename I>
+ConfigWatcher<I>::ConfigWatcher(I& image_ctx)
+  : m_image_ctx(image_ctx) {
+}
+
+template <typename I>
+ConfigWatcher<I>::~ConfigWatcher() {
+  ceph_assert(m_observer == nullptr);
+}
+
+template <typename I>
+void ConfigWatcher<I>::init() {
+  auto cct = m_image_ctx.cct;
+  ldout(cct, 10) << dendl;
+
+  m_observer = new Observer(cct, this);
+  cct->_conf.add_observer(m_observer);
+}
+
+template <typename I>
+void ConfigWatcher<I>::shut_down() {
+  auto cct = m_image_ctx.cct;
+  ldout(cct, 10) << dendl;
+
+  ceph_assert(m_observer != nullptr);
+  cct->_conf.remove_observer(m_observer);
+
+  delete m_observer;
+  m_observer = nullptr;
+}
+
+template <typename I>
+void ConfigWatcher<I>::handle_global_config_change(
+    std::set<std::string> changed_keys) {
+
+  {
+    // ignore any global changes that are being overridden
+    RWLock::RLocker md_locker(m_image_ctx.md_lock);
+    for (auto& key : m_image_ctx.config_overrides) {
+      changed_keys.erase(key);
+    }
+  }
+  if (changed_keys.empty()) {
+    return;
+  }
+
+  auto cct = m_image_ctx.cct;
+  ldout(cct, 10) << "changed_keys=" << changed_keys << dendl;
+
+  // refresh the image to pick up any global config overrides
+  m_image_ctx.state->handle_update_notification();
+}
+
+} // namespace librbd
+
+template class librbd::ConfigWatcher<librbd::ImageCtx>;
diff --git a/src/librbd/ConfigWatcher.h b/src/librbd/ConfigWatcher.h
new file mode 100644 (file)
index 0000000..1f10c8c
--- /dev/null
@@ -0,0 +1,47 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_CONFIG_WATCHER_H
+#define CEPH_LIBRBD_CONFIG_WATCHER_H
+
+#include <set>
+#include <string>
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+template <typename ImageCtxT>
+class ConfigWatcher {
+public:
+  static ConfigWatcher* create(ImageCtxT& image_ctx) {
+    return new ConfigWatcher(image_ctx);
+  }
+
+  ConfigWatcher(ImageCtxT& image_ctx);
+  ~ConfigWatcher();
+
+  ConfigWatcher(const ConfigWatcher&) = delete;
+  ConfigWatcher& operator=(const ConfigWatcher&) = delete;
+
+  void init();
+  void shut_down();
+
+private:
+  struct Observer;
+
+  ImageCtxT& m_image_ctx;
+
+  Observer* m_observer = nullptr;
+
+  void handle_global_config_change(std::set<std::string> changed);
+
+};
+
+} // namespace librbd
+
+extern template class librbd::ConfigWatcher<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_CONFIG_WATCHER_H
index 3c3e9db6c1d7cc1bcf762abca9d06c970537fe0b..39c76678cc4fcba7f9c5f24c0034581bfac259dc 100644 (file)
@@ -43,6 +43,7 @@ target_link_libraries(rbd_test_mock PUBLIC
 set(unittest_librbd_srcs
   test_main.cc
   test_mock_fixture.cc
+  test_mock_ConfigWatcher.cc
   test_mock_DeepCopyRequest.cc
   test_mock_ExclusiveLock.cc
   test_mock_Journal.cc
index 2a46c89a44fdc579b061c575410f29850c8cfcb5..01291f045683b1ef9c97a4138e2cebc8eac08bb9 100644 (file)
@@ -312,6 +312,7 @@ struct MockImageCtx {
   bool cache;
 
   ConfigProxy config;
+  std::set<std::string> config_overrides;
 };
 
 } // namespace librbd
index a817d333e1d5f5434678e803c14300b365628eac..8e399b6c67ce118780ccc3f509fc10b461d6244b 100644 (file)
@@ -25,6 +25,8 @@ struct MockImageState {
 
   MOCK_METHOD1(prepare_lock, void(Context*));
   MOCK_METHOD0(handle_prepare_lock_complete, void());
+
+  MOCK_METHOD0(handle_update_notification, void());
 };
 
 } // namespace librbd
diff --git a/src/test/librbd/test_mock_ConfigWatcher.cc b/src/test/librbd/test_mock_ConfigWatcher.cc
new file mode 100644 (file)
index 0000000..264e3d2
--- /dev/null
@@ -0,0 +1,100 @@
+// -*- 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 "include/rbd_types.h"
+#include "common/ceph_mutex.h"
+#include "librbd/ConfigWatcher.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include <list>
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+  MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+  }
+};
+
+} // anonymous namespace
+} // namespace librbd
+
+#include "librbd/ConfigWatcher.cc"
+
+namespace librbd {
+
+using ::testing::Invoke;
+
+class TestMockConfigWatcher : public TestMockFixture {
+public:
+  typedef ConfigWatcher<MockTestImageCtx> MockConfigWatcher;
+
+  librbd::ImageCtx *m_image_ctx;
+
+  ceph::mutex m_lock = ceph::make_mutex("m_lock");
+  ceph::condition_variable m_cv;
+  bool m_refreshed = false;
+
+  void SetUp() override {
+    TestMockFixture::SetUp();
+
+    ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
+  }
+
+  void expect_update_notification(MockTestImageCtx& mock_image_ctx) {
+    EXPECT_CALL(*mock_image_ctx.state, handle_update_notification())
+      .WillOnce(Invoke([this]() {
+          std::unique_lock locker{m_lock};
+          m_refreshed = true;
+          m_cv.notify_all();
+        }));
+  }
+
+  void wait_for_update_notification() {
+    std::unique_lock locker{m_lock};
+    m_cv.wait(locker, [this] {
+        if (m_refreshed) {
+          m_refreshed = false;
+          return true;
+        }
+        return false;
+      });
+  }
+};
+
+TEST_F(TestMockConfigWatcher, GlobalConfig) {
+  MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+  MockConfigWatcher mock_config_watcher(mock_image_ctx);
+  mock_config_watcher.init();
+
+  expect_update_notification(mock_image_ctx);
+  mock_image_ctx.cct->_conf.set_val("rbd_cache", "false");
+  mock_image_ctx.cct->_conf.set_val("rbd_cache", "true");
+  mock_image_ctx.cct->_conf.apply_changes(nullptr);
+  wait_for_update_notification();
+
+  mock_config_watcher.shut_down();
+}
+
+TEST_F(TestMockConfigWatcher, IgnoreOverriddenGlobalConfig) {
+  MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+  MockConfigWatcher mock_config_watcher(mock_image_ctx);
+  mock_config_watcher.init();
+
+  EXPECT_CALL(*mock_image_ctx.state, handle_update_notification())
+    .Times(0);
+  mock_image_ctx.config_overrides.insert("rbd_cache");
+  mock_image_ctx.cct->_conf.set_val("rbd_cache", "false");
+  mock_image_ctx.cct->_conf.set_val("rbd_cache", "true");
+  mock_image_ctx.cct->_conf.apply_changes(nullptr);
+
+  mock_config_watcher.shut_down();
+
+  ASSERT_FALSE(m_refreshed);
+}
+
+} // namespace librbd