]> git-server-git.apps.pok.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, 29 Jul 2020 12:45:51 +0000 (08:45 -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>
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 5f9063042ad796b264a03352cc9f00b6565a4599..00fcc1d75719dc42bbf8f495f752c97bfd3907b6 100644 (file)
@@ -9,6 +9,7 @@ set(librbd_internal_srcs
   AsioEngine.cc
   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..0e41278
--- /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
+    std::shared_lock image_locker{m_image_ctx.image_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 8189b46d7555917b5686243be28ec69cdc9e9708..403c3c12ccdfe49c00a50fb51114c34c16309519 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 a06ce09e6a5dc966232198a7c2a4111a7ca28618..82acb2623b3497327e3695ee4e8f242dc6e66985 100644 (file)
@@ -329,6 +329,7 @@ struct MockImageCtx {
   bool cache;
 
   ConfigProxy config;
+  std::set<std::string> config_overrides;
 };
 
 } // namespace librbd
index 9b9dc8b879a55e5eacbcc475f56266df2f46b7cf..f510c4a0fd835be18ae41f8d49f20d35ed452f6e 100644 (file)
@@ -30,6 +30,8 @@ struct MockImageState {
 
   MOCK_METHOD2(register_update_watcher, int(UpdateWatchCtx *, uint64_t *));
   MOCK_METHOD2(unregister_update_watcher, void(uint64_t, Context *));
+
+  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