From 184f6518ea0b4a820c87b8991c19c78dc0f82d1b Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 16 Feb 2021 18:03:38 -0500 Subject: [PATCH] mgr_module: get_foreign_ceph_option(entity, name) 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 (cherry picked from commit 02c44b1734d0c162acd77fb7989422591fbb3c8f) --- src/mgr/ActivePyModules.cc | 97 ++++++++++++++++++++++++++++++++++ src/mgr/ActivePyModules.h | 3 ++ src/mgr/BaseMgrModule.cc | 15 ++++++ src/pybind/mgr/ceph_module.pyi | 1 + src/pybind/mgr/mgr_module.py | 3 ++ 5 files changed, 119 insertions(+) diff --git a/src/mgr/ActivePyModules.cc b/src/mgr/ActivePyModules.cc index 10fde947474..19fcd433db3 100644 --- a/src/mgr/ActivePyModules.cc +++ b/src/mgr/ActivePyModules.cc @@ -27,6 +27,7 @@ // For ::config_prefix #include "PyModule.h" #include "PyModuleRegistry.h" +#include "PyUtil.h" #include "ActivePyModules.h" #include "DaemonKey.h" @@ -1038,6 +1039,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> config; + cluster_state.with_osdmap([&](const OSDMap &osdmap) { + map 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> 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(&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) { diff --git a/src/mgr/ActivePyModules.h b/src/mgr/ActivePyModules.h index db2cd03aebf..2fab4ca4635 100644 --- a/src/mgr/ActivePyModules.h +++ b/src/mgr/ActivePyModules.h @@ -136,6 +136,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); diff --git a/src/mgr/BaseMgrModule.cc b/src/mgr/BaseMgrModule.cc index ede9a809b21..f368381e4fc 100644 --- a/src/mgr/BaseMgrModule.cc +++ b/src/mgr/BaseMgrModule.cc @@ -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"}, diff --git a/src/pybind/mgr/ceph_module.pyi b/src/pybind/mgr/ceph_module.pyi index a5622065c79..77ed08c033b 100644 --- a/src/pybind/mgr/ceph_module.pyi +++ b/src/pybind/mgr/ceph_module.pyi @@ -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):... diff --git a/src/pybind/mgr/mgr_module.py b/src/pybind/mgr/mgr_module.py index 6f472513bb7..e956c8a1c3a 100644 --- a/src/pybind/mgr/mgr_module.py +++ b/src/pybind/mgr/mgr_module.py @@ -1351,6 +1351,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 -- 2.47.3