]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr_module: get_foreign_ceph_option(entity, name)
authorSage Weil <sage@newdream.net>
Tue, 16 Feb 2021 23:03:38 +0000 (18:03 -0500)
committerSage Weil <sage@newdream.net>
Wed, 17 Feb 2021 17:27:58 +0000 (12:27 -0500)
Fetch a config value that applies to an arbitrary daemon in the cluster.

Initial implementaiton is not very efficient, but it is better than a
round-trip query to the monitor.

Note: this currently excludes mgr/ options (which should hopefully not be
of interest on other daemons!).

Signed-off-by: Sage Weil <sage@newdream.net>
src/mgr/ActivePyModules.cc
src/mgr/ActivePyModules.h
src/mgr/BaseMgrModule.cc
src/pybind/mgr/ceph_module.pyi
src/pybind/mgr/mgr_module.py

index 6ca2b49e2fdf0fb3fd1adee0e867d9eca1ee3aa2..36d3718b24efbeeeaa09e2fb6022b59af68f8d20 100644 (file)
@@ -27,6 +27,7 @@
 // For ::config_prefix
 #include "PyModule.h"
 #include "PyModuleRegistry.h"
+#include "PyUtil.h"
 
 #include "ActivePyModules.h"
 #include "DaemonKey.h"
@@ -1070,6 +1071,102 @@ PyObject *ActivePyModules::get_osdmap()
   return construct_with_capsule("mgr_module", "OSDMap", (void*)newmap);
 }
 
+PyObject *ActivePyModules::get_foreign_config(
+  const std::string& who,
+  const std::string& name)
+{
+  dout(10) << "ceph_foreign_option_get " << who << " " << name << dendl;
+
+  // NOTE: for now this will only work with build-in options, not module options.
+  const Option *opt = g_conf().find_option(name);
+  if (!opt) {
+    dout(4) << "ceph_foreign_option_get " << name << " not found " << dendl;
+    PyErr_Format(PyExc_KeyError, "option not found: %s", name.c_str());
+    return nullptr;
+  }
+
+  // If the monitors are not yet running pacific, we cannot rely on our local
+  // ConfigMap
+  if (!have_local_config_map) {
+    dout(20) << "mon cluster wasn't pacific when we started: falling back to 'config get'"
+            << dendl;
+    without_gil_t no_gil;
+    Command cmd;
+    {
+      std::lock_guard l(lock);
+      cmd.run(
+       &monc,
+       "{\"prefix\": \"config get\","s +
+       "\"who\": \""s + who + "\","s +
+       "\"key\": \""s + name + "\"}");
+    }
+    cmd.wait();
+    dout(10) << "ceph_foreign_option_get (mon command) " << who << " " << name << " = "
+            << cmd.outbl.to_str() << dendl;
+    with_gil_t gil(no_gil);
+    return get_python_typed_option_value(opt->type, cmd.outbl.to_str());
+  }
+
+  // mimic the behavor of mon/ConfigMonitor's 'config get' command
+  EntityName entity;
+  if (!entity.from_str(who) &&
+      !entity.from_str(who + ".")) {
+    dout(5) << "unrecognized entity '" << who << "'" << dendl;
+    PyErr_Format(PyExc_KeyError, "invalid entity: %s", who.c_str());
+    return nullptr;
+  }
+
+  without_gil_t no_gil;
+  lock.lock();
+
+  // FIXME: this is super inefficient, since we generate the entire daemon
+  // config just to extract one value from it!
+
+  std::map<std::string,std::string,std::less<>> config;
+  cluster_state.with_osdmap([&](const OSDMap &osdmap) {
+      map<string,string> crush_location;
+      string device_class;
+      if (entity.is_osd()) {
+       osdmap.crush->get_full_location(who, &crush_location);
+       int id = atoi(entity.get_id().c_str());
+       const char *c = osdmap.crush->get_item_class(id);
+       if (c) {
+         device_class = c;
+       }
+       dout(10) << __func__ << " crush_location " << crush_location
+                << " class " << device_class << dendl;
+      }
+
+      std::map<std::string,pair<std::string,const MaskedOption*>> src;
+      config = config_map.generate_entity_map(
+       entity,
+       crush_location,
+       osdmap.crush.get(),
+       device_class,
+       &src);
+    });
+
+  // get a single value
+  string value;
+  auto p = config.find(name);
+  if (p != config.end()) {
+    value = p->second;
+  } else {
+    if (!entity.is_client() &&
+       !boost::get<boost::blank>(&opt->daemon_value)) {
+      value = Option::to_str(opt->daemon_value);
+    } else {
+      value = Option::to_str(opt->value);
+    }
+  }
+
+  dout(10) << "ceph_foreign_option_get (configmap) " << who << " " << name << " = "
+          << value << dendl;
+  lock.unlock();
+  with_gil_t with_gil(no_gil);
+  return get_python_typed_option_value(opt->type, value);
+}
+
 void ActivePyModules::set_health_checks(const std::string& module_name,
                                  health_check_map_t&& checks)
 {
index d28ffa054e42c256c3d28ff7c7c560b616ba9692..09c7041a610130b9729625b6eefedddd8f7d6a8e 100644 (file)
@@ -137,6 +137,9 @@ public:
   PyObject *get_typed_config(const std::string &module_name,
                             const std::string &key,
                             const std::string &prefix = "") const;
+  PyObject *get_foreign_config(
+    const std::string& who,
+    const std::string& name);
 
   void set_health_checks(const std::string& module_name,
                         health_check_map_t&& checks);
index ede9a809b2143bd5ff3e4164ddead5abc018eeae..f368381e4fc9ba7dbdf0856c75eab6ef17c2c8c5 100644 (file)
@@ -432,6 +432,18 @@ ceph_option_get(BaseMgrModule *self, PyObject *args)
   }
 }
 
+static PyObject*
+ceph_foreign_option_get(BaseMgrModule *self, PyObject *args)
+{
+  char *who = nullptr;
+  char *what = nullptr;
+  if (!PyArg_ParseTuple(args, "ss:ceph_foreign_option_get", &who, &what)) {
+    derr << "Invalid args!" << dendl;
+    return nullptr;
+  }
+  return self->py_modules->get_foreign_config(who, what);
+}
+
 static PyObject*
 ceph_get_module_option(BaseMgrModule *self, PyObject *args)
 {
@@ -1389,6 +1401,9 @@ PyMethodDef BaseMgrModule_methods[] = {
   {"_ceph_get_option", (PyCFunction)ceph_option_get, METH_VARARGS,
    "Get a native configuration option value"},
 
+  {"_ceph_get_foreign_option", (PyCFunction)ceph_foreign_option_get, METH_VARARGS,
+   "Get a native configuration option value for another entity"},
+
   {"_ceph_get_module_option", (PyCFunction)ceph_get_module_option, METH_VARARGS,
    "Get a module configuration option value"},
 
index a5622065c79a16e3ad29cf699f0c774f0f9e384b..77ed08c033b6d2834f11ad6d12cbb83c7dfc2823 100644 (file)
@@ -54,6 +54,7 @@ class BaseMgrModule(object):
     def _ceph_set_health_checks(self, checks):...
     def _ceph_get_mgr_id(self):...
     def _ceph_get_option(self, key):...
+    def _ceph_get_foreign_option(self, entity, key):...
     def _ceph_get_module_option(self, key, default, localized_prefix=""):...
     def _ceph_get_store_prefix(self, key_prefix):...
     def _ceph_set_module_option(self, module, key, val):...
index 8d52243f503516c6ce4263d6ea244548539c0c71..b85afd352c2a218896be69b3a1e7810054614c06 100644 (file)
@@ -1358,6 +1358,9 @@ class MgrModule(ceph_module.BaseMgrModule, MgrModuleLoggingMixin):
     def get_ceph_option(self, key: str) -> OptionValue:
         return self._ceph_get_option(key)
 
+    def get_foreign_ceph_option(self, entity: str, key: str) -> OptionValue:
+        return self._ceph_get_foreign_option(entity, key)
+
     def _validate_module_option(self, key: str) -> None:
         """
         Helper: don't allow get/set config callers to