]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/mgr/ActivePyModules.cc
import 15.2.5
[ceph.git] / ceph / src / mgr / ActivePyModules.cc
index 9e32dce58cac9200c47e77bc1f1a0636960c018e..210f7755090857e448761c9d11170dd7dad2452a 100644 (file)
 #include "PyModuleRegistry.h"
 
 #include "ActivePyModules.h"
+#include "DaemonKey.h"
 #include "DaemonServer.h"
 
 #define dout_context g_ceph_context
 #define dout_subsys ceph_subsys_mgr
 #undef dout_prefix
-#define dout_prefix *_dout << "mgr " << __func__ << " "    
+#define dout_prefix *_dout << "mgr " << __func__ << " "
 
 ActivePyModules::ActivePyModules(PyModuleConfig &module_config_,
           std::map<std::string, std::string> store_data,
@@ -46,9 +47,11 @@ ActivePyModules::ActivePyModules(PyModuleConfig &module_config_,
   : module_config(module_config_), daemon_state(ds), cluster_state(cs),
     monc(mc), clog(clog_), audit_clog(audit_clog_), objecter(objecter_),
     client(client_), finisher(f),
-    server(server), py_module_registry(pmr), lock("ActivePyModules")
+    cmd_finisher(g_ceph_context, "cmd_finisher", "cmdfin"),
+    server(server), py_module_registry(pmr)
 {
   store_cache = std::move(store_data);
+  cmd_finisher.start();
 }
 
 ActivePyModules::~ActivePyModules() = default;
@@ -61,23 +64,19 @@ void ActivePyModules::dump_server(const std::string &hostname,
   f->open_array_section("services");
   std::string ceph_version;
 
-  for (const auto &i : dmc) {
-    std::lock_guard l(i.second->lock);
-    const auto &key = i.first;
-    const std::string &str_type = key.first;
-    const std::string &svc_name = key.second;
-
+  for (const auto &[key, state] : dmc) {
+    std::lock_guard l(state->lock);
     // TODO: pick the highest version, and make sure that
     // somewhere else (during health reporting?) we are
     // indicating to the user if we see mixed versions
-    auto ver_iter = i.second->metadata.find("ceph_version");
-    if (ver_iter != i.second->metadata.end()) {
-      ceph_version = i.second->metadata.at("ceph_version");
+    auto ver_iter = state->metadata.find("ceph_version");
+    if (ver_iter != state->metadata.end()) {
+      ceph_version = state->metadata.at("ceph_version");
     }
 
     f->open_object_section("service");
-    f->dump_string("type", str_type);
-    f->dump_string("id", svc_name);
+    f->dump_string("type", key.type);
+    f->dump_string("id", key.name);
     f->close_section();
   }
   f->close_section();
@@ -128,7 +127,7 @@ PyObject *ActivePyModules::get_metadata_python(
   const std::string &svc_type,
   const std::string &svc_id)
 {
-  auto metadata = daemon_state.get(DaemonKey(svc_type, svc_id));
+  auto metadata = daemon_state.get(DaemonKey{svc_type, svc_id});
   if (metadata == nullptr) {
     derr << "Requested missing service " << svc_type << "." << svc_id << dendl;
     Py_RETURN_NONE;
@@ -148,7 +147,7 @@ PyObject *ActivePyModules::get_daemon_status_python(
   const std::string &svc_type,
   const std::string &svc_id)
 {
-  auto metadata = daemon_state.get(DaemonKey(svc_type, svc_id));
+  auto metadata = daemon_state.get(DaemonKey{svc_type, svc_id});
   if (metadata == nullptr) {
     derr << "Requested missing service " << svc_type << "." << svc_id << dendl;
     Py_RETURN_NONE;
@@ -184,7 +183,7 @@ PyObject *ActivePyModules::get_python(const std::string &what)
       osd_map.crush->encode(rdata, CEPH_FEATURES_SUPPORTED_DEFAULT);
     });
     std::string crush_text = rdata.to_str();
-    return PyString_FromString(crush_text.c_str());
+    return PyUnicode_FromString(crush_text.c_str());
   } else if (what.substr(0, 7) == "osd_map") {
     cluster_state.with_osdmap([&f, &what, &tstate](const OSDMap &osd_map){
       PyEval_RestoreThread(tstate);
@@ -197,6 +196,22 @@ PyObject *ActivePyModules::get_python(const std::string &what)
       }
     });
     return f.get();
+  } else if (what == "modified_config_options") {
+    PyEval_RestoreThread(tstate);
+    auto all_daemons = daemon_state.get_all();
+    set<string> names;
+    for (auto& [key, daemon] : all_daemons) {
+      std::lock_guard l(daemon->lock);
+      for (auto& [name, valmap] : daemon->config) {
+       names.insert(name);
+      }
+    }
+    f.open_array_section("options");
+    for (auto& name : names) {
+      f.dump_string("name", name);
+    }
+    f.close_section();
+    return f.get();
   } else if (what.substr(0, 6) == "config") {
     PyEval_RestoreThread(tstate);
     if (what == "config_options") {
@@ -225,12 +240,26 @@ PyObject *ActivePyModules::get_python(const std::string &what)
     auto dmc = daemon_state.get_by_service("osd");
     PyEval_RestoreThread(tstate);
 
-    for (const auto &i : dmc) {
-      std::lock_guard l(i.second->lock);
-      f.open_object_section(i.first.second.c_str());
-      f.dump_string("hostname", i.second->hostname);
-      for (const auto &j : i.second->metadata) {
-        f.dump_string(j.first.c_str(), j.second);
+    for (const auto &[key, state] : dmc) {
+      std::lock_guard l(state->lock);
+      f.open_object_section(key.name.c_str());
+      f.dump_string("hostname", state->hostname);
+      for (const auto &[name, val] : state->metadata) {
+        f.dump_string(name.c_str(), val);
+      }
+      f.close_section();
+    }
+    return f.get();
+  } else if (what == "mds_metadata") {
+    auto dmc = daemon_state.get_by_service("mds");
+    PyEval_RestoreThread(tstate);
+
+    for (const auto &[key, state] : dmc) {
+      std::lock_guard l(state->lock);
+      f.open_object_section(key.name.c_str());
+      f.dump_string("hostname", state->hostname);
+      for (const auto &[name, val] : state->metadata) {
+        f.dump_string(name.c_str(), val);
       }
       f.close_section();
     }
@@ -294,7 +323,7 @@ PyObject *ActivePyModules::get_python(const std::string &what)
     cluster_state.with_pgmap(
       [&f, &tstate](const PGMap &pg_map) {
         PyEval_RestoreThread(tstate);
-       pg_map.dump(&f);
+       pg_map.dump(&f, false);
       }
     );
     return f.get();
@@ -312,10 +341,15 @@ PyObject *ActivePyModules::get_python(const std::string &what)
   } else if (what.size() > 7 &&
             what.substr(0, 7) == "device ") {
     string devid = what.substr(7);
-    daemon_state.with_device(devid, [&f, &tstate] (const DeviceState& dev) {
-        PyEval_RestoreThread(tstate);
-       f.dump_object("device", dev);
-      });
+    if (!daemon_state.with_device(
+         devid,
+         [&f, &tstate] (const DeviceState& dev) {
+           PyEval_RestoreThread(tstate);
+           f.dump_object("device", dev);
+         })) {
+      // device not found
+      PyEval_RestoreThread(tstate);
+    }
     return f.get();
   } else if (what == "io_rate") {
     cluster_state.with_pgmap(
@@ -327,7 +361,7 @@ PyObject *ActivePyModules::get_python(const std::string &what)
     return f.get();
   } else if (what == "df") {
     cluster_state.with_osdmap_and_pgmap(
-      [this, &f, &tstate](
+      [&f, &tstate](
        const OSDMap& osd_map,
        const PGMap &pg_map) {
        PyEval_RestoreThread(tstate);
@@ -335,16 +369,40 @@ PyObject *ActivePyModules::get_python(const std::string &what)
         pg_map.dump_pool_stats_full(osd_map, nullptr, &f, true);
       });
     return f.get();
+  } else if (what == "pg_stats") {
+    cluster_state.with_pgmap(
+        [&f, &tstate](const PGMap &pg_map) {
+      PyEval_RestoreThread(tstate);
+      pg_map.dump_pg_stats(&f, false);
+    });
+    return f.get();
+  } else if (what == "pool_stats") {
+    cluster_state.with_pgmap(
+        [&f, &tstate](const PGMap &pg_map) {
+      PyEval_RestoreThread(tstate);
+      pg_map.dump_pool_stats(&f);
+    });
+    return f.get();
+  } else if (what == "pg_ready") {
+    PyEval_RestoreThread(tstate);
+    server.dump_pg_ready(&f);
+    return f.get();
   } else if (what == "osd_stats") {
     cluster_state.with_pgmap(
         [&f, &tstate](const PGMap &pg_map) {
       PyEval_RestoreThread(tstate);
-      pg_map.dump_osd_stats(&f);
+      pg_map.dump_osd_stats(&f, false);
+    });
+    return f.get();
+  } else if (what == "osd_ping_times") {
+    cluster_state.with_pgmap(
+        [&f, &tstate](const PGMap &pg_map) {
+      PyEval_RestoreThread(tstate);
+      pg_map.dump_osd_ping_times(&f);
     });
     return f.get();
   } else if (what == "osd_pool_stats") {
     int64_t poolid = -ENOENT;
-    string pool_name;
     cluster_state.with_osdmap_and_pgmap([&](const OSDMap& osdmap,
                                            const PGMap& pg_map) {
         PyEval_RestoreThread(tstate);
@@ -356,18 +414,19 @@ PyObject *ActivePyModules::get_python(const std::string &what)
         f.close_section();
     });
     return f.get();
-  } else if (what == "health" || what == "mon_status") {
-    bufferlist json;
-    if (what == "health") {
-      json = cluster_state.get_health();
-    } else if (what == "mon_status") {
-      json = cluster_state.get_mon_status();
-    } else {
-      ceph_abort();
-    }
-
-    PyEval_RestoreThread(tstate);
-    f.dump_string("json", json.to_str());
+  } else if (what == "health") {
+    cluster_state.with_health(
+        [&f, &tstate](const ceph::bufferlist &health_json) {
+      PyEval_RestoreThread(tstate);
+      f.dump_string("json", health_json.to_str());
+    });
+    return f.get();
+  } else if (what == "mon_status") {
+    cluster_state.with_mon_status(
+        [&f, &tstate](const ceph::bufferlist &mon_status_json) {
+      PyEval_RestoreThread(tstate);
+      f.dump_string("json", mon_status_json.to_str());
+    });
     return f.get();
   } else if (what == "mgr_map") {
     cluster_state.with_mgrmap([&f, &tstate](const MgrMap &mgr_map) {
@@ -386,22 +445,23 @@ void ActivePyModules::start_one(PyModuleRef py_module)
 {
   std::lock_guard l(lock);
 
-  ceph_assert(modules.count(py_module->get_name()) == 0);
-
   const auto name = py_module->get_name();
-  modules[name].reset(new ActivePyModule(py_module, clog));
-  auto active_module = modules.at(name).get();
+  auto active_module = std::make_shared<ActivePyModule>(py_module, clog);
 
+  pending_modules.insert(name);
   // Send all python calls down a Finisher to avoid blocking
   // C++ code, and avoid any potential lock cycles.
-  finisher.queue(new FunctionContext([this, active_module, name](int) {
+  finisher.queue(new LambdaContext([this, active_module, name](int) {
     int r = active_module->load(this);
+    std::lock_guard l(lock);
+    pending_modules.erase(name);
     if (r != 0) {
       derr << "Failed to run module in active mode ('" << name << "')"
            << dendl;
-      std::lock_guard l(lock);
-      modules.erase(name);
     } else {
+      auto em = modules.emplace(name, active_module);
+      ceph_assert(em.second); // actually inserted
+
       dout(4) << "Starting thread for " << name << dendl;
       active_module->thread.create(active_module->get_thread_name());
     }
@@ -413,27 +473,27 @@ void ActivePyModules::shutdown()
   std::lock_guard locker(lock);
 
   // Signal modules to drop out of serve() and/or tear down resources
-  for (auto &i : modules) {
-    auto module = i.second.get();
-    const auto& name = i.first;
-
-    lock.Unlock();
+  for (auto& [name, module] : modules) {
+    lock.unlock();
     dout(10) << "calling module " << name << " shutdown()" << dendl;
     module->shutdown();
     dout(10) << "module " << name << " shutdown() returned" << dendl;
-    lock.Lock();
+    lock.lock();
   }
 
   // For modules implementing serve(), finish the threads where we
   // were running that.
-  for (auto &i : modules) {
-    lock.Unlock();
-    dout(10) << "joining module " << i.first << dendl;
-    i.second->thread.join();
-    dout(10) << "joined module " << i.first << dendl;
-    lock.Lock();
+  for (auto& [name, module] : modules) {
+    lock.unlock();
+    dout(10) << "joining module " << name << dendl;
+    module->thread.join();
+    dout(10) << "joined module " << name << dendl;
+    lock.lock();
   }
 
+  cmd_finisher.wait_for_empty();
+  cmd_finisher.stop();
+
   modules.clear();
 }
 
@@ -443,12 +503,14 @@ void ActivePyModules::notify_all(const std::string &notify_type,
   std::lock_guard l(lock);
 
   dout(10) << __func__ << ": notify_all " << notify_type << dendl;
-  for (auto& i : modules) {
-    auto module = i.second.get();
+  for (auto& [name, module] : modules) {
     // Send all python calls down a Finisher to avoid blocking
     // C++ code, and avoid any potential lock cycles.
-    finisher.queue(new FunctionContext([module, notify_type, notify_id](int r){
-      module->notify(notify_type, notify_id);
+    dout(15) << "queuing notify to " << name << dendl;
+    // workaround for https://bugs.llvm.org/show_bug.cgi?id=35984
+    finisher.queue(new LambdaContext([module=module, notify_type, notify_id]
+      (int r){ 
+        module->notify(notify_type, notify_id); 
     }));
   }
 }
@@ -458,15 +520,16 @@ void ActivePyModules::notify_all(const LogEntry &log_entry)
   std::lock_guard l(lock);
 
   dout(10) << __func__ << ": notify_all (clog)" << dendl;
-  for (auto& i : modules) {
-    auto module = i.second.get();
+  for (auto& [name, module] : modules) {
     // Send all python calls down a Finisher to avoid blocking
     // C++ code, and avoid any potential lock cycles.
     //
     // Note intentional use of non-reference lambda binding on
     // log_entry: we take a copy because caller's instance is
     // probably ephemeral.
-    finisher.queue(new FunctionContext([module, log_entry](int r){
+    dout(15) << "queuing notify (clog) to " << name << dendl;
+    // workaround for https://bugs.llvm.org/show_bug.cgi?id=35984
+    finisher.queue(new LambdaContext([module=module, log_entry](int r){
       module->notify_clog(log_entry);
     }));
   }
@@ -512,10 +575,10 @@ bool ActivePyModules::get_config(const std::string &module_name,
   const std::string global_key = PyModule::config_prefix
     + module_name + "/" + key;
 
-  dout(4) << __func__ << " key: " << global_key << dendl;
+  dout(20) << " key: " << global_key << dendl;
 
   std::lock_guard lock(module_config.lock);
-  
+
   auto i = module_config.config.find(global_key);
   if (i != module_config.config.end()) {
     *val = i->second;
@@ -554,10 +617,10 @@ PyObject *ActivePyModules::get_typed_config(
   }
   PyEval_RestoreThread(tstate);
   if (prefix.size()) {
-    dout(4) << __func__ << " [" << prefix << "/]" << key << " not found "
+    dout(10) << " [" << prefix << "/]" << key << " not found "
            << dendl;
   } else {
-    dout(4) << __func__ << " " << key << " not found " << dendl;
+    dout(10) << " " << key << " not found " << dendl;
   }
   Py_RETURN_NONE;
 }
@@ -576,7 +639,7 @@ PyObject *ActivePyModules::get_store_prefix(const std::string &module_name,
   dout(4) << __func__ << " prefix: " << global_prefix << dendl;
 
   PyFormatter f;
-  
+
   for (auto p = store_cache.lower_bound(global_prefix);
        p != store_cache.end() && p->first.find(global_prefix) == 0;
        ++p) {
@@ -590,13 +653,10 @@ void ActivePyModules::set_store(const std::string &module_name,
 {
   const std::string global_key = PyModule::config_prefix
                                    + module_name + "/" + key;
-  
+
   Command set_cmd;
   {
-    PyThreadState *tstate = PyEval_SaveThread();
     std::lock_guard l(lock);
-    PyEval_RestoreThread(tstate);
-
     if (val) {
       store_cache[global_key] = *val;
     } else {
@@ -640,11 +700,10 @@ std::map<std::string, std::string> ActivePyModules::get_services() const
 {
   std::map<std::string, std::string> result;
   std::lock_guard l(lock);
-  for (const auto& i : modules) {
-    const auto &module = i.second.get();
+  for (const auto& [name, module] : modules) {
     std::string svc_str = module->get_uri();
     if (!svc_str.empty()) {
-      result[module->get_name()] = svc_str;
+      result[name] = svc_str;
     }
   }
 
@@ -664,7 +723,7 @@ PyObject* ActivePyModules::with_perf_counters(
   PyFormatter f;
   f.open_array_section(path.c_str());
 
-  auto metadata = daemon_state.get(DaemonKey(svc_name, svc_id));
+  auto metadata = daemon_state.get(DaemonKey{svc_name, svc_id});
   if (metadata) {
     std::lock_guard l2(metadata->lock);
     if (metadata->perf_counters.instances.count(path)) {
@@ -701,7 +760,7 @@ PyObject* ActivePyModules::get_counter_python(
       const auto &avg_data = counter_instance.get_data_avg();
       for (const auto &datapoint : avg_data) {
         f.open_array_section("datapoint");
-        f.dump_unsigned("t", datapoint.t.sec());
+        f.dump_float("t", datapoint.t);
         f.dump_unsigned("s", datapoint.s);
         f.dump_unsigned("c", datapoint.c);
         f.close_section();
@@ -710,7 +769,7 @@ PyObject* ActivePyModules::get_counter_python(
       const auto &data = counter_instance.get_data();
       for (const auto &datapoint : data) {
         f.open_array_section("datapoint");
-        f.dump_unsigned("t", datapoint.t.sec());
+        f.dump_float("t", datapoint.t);
         f.dump_unsigned("v", datapoint.v);
         f.close_section();
       }
@@ -731,12 +790,12 @@ PyObject* ActivePyModules::get_latest_counter_python(
   {
     if (counter_type.type & PERFCOUNTER_LONGRUNAVG) {
       const auto &datapoint = counter_instance.get_latest_data_avg();
-      f.dump_unsigned("t", datapoint.t.sec());
+      f.dump_float("t", datapoint.t);
       f.dump_unsigned("s", datapoint.s);
       f.dump_unsigned("c", datapoint.c);
     } else {
       const auto &datapoint = counter_instance.get_latest_data();
-      f.dump_unsigned("t", datapoint.t.sec());
+      f.dump_float("t", datapoint.t);
       f.dump_unsigned("v", datapoint.v);
     }
   };
@@ -758,7 +817,7 @@ PyObject* ActivePyModules::get_perf_schema_python(
   } else if (svc_id.empty()) {
     daemons = daemon_state.get_by_service(svc_type);
   } else {
-    auto key = DaemonKey(svc_type, svc_id);
+    auto key = DaemonKey{svc_type, svc_id};
     // so that the below can be a loop in all cases
     auto got = daemon_state.get(key);
     if (got != nullptr) {
@@ -768,13 +827,8 @@ PyObject* ActivePyModules::get_perf_schema_python(
 
   PyFormatter f;
   if (!daemons.empty()) {
-    for (auto statepair : daemons) {
-      auto key = statepair.first;
-      auto state = statepair.second;
-
-      std::ostringstream daemon_name;
-      daemon_name << key.first << "." << key.second;
-      f.open_object_section(daemon_name.str().c_str());
+    for (auto& [key, state] : daemons) {
+      f.open_object_section(ceph::to_string(key).c_str());
 
       std::lock_guard l(state->lock);
       for (auto ctr_inst_iter : state->perf_counters.instances) {
@@ -878,12 +932,12 @@ void ActivePyModules::set_health_checks(const std::string& module_name,
 {
   bool changed = false;
 
-  lock.Lock();
+  lock.lock();
   auto p = modules.find(module_name);
   if (p != modules.end()) {
     changed = p->second->set_health_checks(std::move(checks));
   }
-  lock.Unlock();
+  lock.unlock();
 
   // immediately schedule a report to be sent to the monitors with the new
   // health checks that have changed. This is done asynchronusly to avoid
@@ -902,28 +956,32 @@ void ActivePyModules::set_health_checks(const std::string& module_name,
 }
 
 int ActivePyModules::handle_command(
-  std::string const &module_name,
+  const ModuleCommand& module_command,
+  const MgrSession& session,
   const cmdmap_t &cmdmap,
   const bufferlist &inbuf,
   std::stringstream *ds,
   std::stringstream *ss)
 {
-  lock.Lock();
-  auto mod_iter = modules.find(module_name);
+  lock.lock();
+  auto mod_iter = modules.find(module_command.module_name);
   if (mod_iter == modules.end()) {
-    *ss << "Module '" << module_name << "' is not available";
+    *ss << "Module '" << module_command.module_name << "' is not available";
+    lock.unlock();
     return -ENOENT;
   }
 
-  lock.Unlock();
-  return mod_iter->second->handle_command(cmdmap, inbuf, ds, ss);
+  lock.unlock();
+  return mod_iter->second->handle_command(module_command, session, cmdmap,
+                                          inbuf, ds, ss);
 }
 
 void ActivePyModules::get_health_checks(health_check_map_t *checks)
 {
   std::lock_guard l(lock);
-  for (auto& p : modules) {
-    p.second->get_health_checks(checks);
+  for (auto& [name, module] : modules) {
+    dout(15) << "getting health checks for " << name << dendl;
+    module->get_health_checks(checks);
   }
 }
 
@@ -959,13 +1017,14 @@ void ActivePyModules::get_progress_events(std::map<std::string,ProgressEvent> *e
 void ActivePyModules::config_notify()
 {
   std::lock_guard l(lock);
-  for (auto& i : modules) {
-    auto module = i.second.get();
+  for (auto& [name, module] : modules) {
     // Send all python calls down a Finisher to avoid blocking
     // C++ code, and avoid any potential lock cycles.
-    finisher.queue(new FunctionContext([module](int r){
-                                        module->config_notify();
-                                      }));
+    dout(15) << "notify (config) " << name << dendl;
+    // workaround for https://bugs.llvm.org/show_bug.cgi?id=35984
+    finisher.queue(new LambdaContext([module=module](int r){ 
+      module->config_notify();
+    }));
   }
 }
 
@@ -976,17 +1035,17 @@ void ActivePyModules::set_uri(const std::string& module_name,
 
   dout(4) << " module " << module_name << " set URI '" << uri << "'" << dendl;
 
-  modules[module_name]->set_uri(uri);
+  modules.at(module_name)->set_uri(uri);
 }
 
-OSDPerfMetricQueryID ActivePyModules::add_osd_perf_query(
+MetricQueryID ActivePyModules::add_osd_perf_query(
     const OSDPerfMetricQuery &query,
     const std::optional<OSDPerfMetricLimit> &limit)
 {
   return server.add_osd_perf_query(query, limit);
 }
 
-void ActivePyModules::remove_osd_perf_query(OSDPerfMetricQueryID query_id)
+void ActivePyModules::remove_osd_perf_query(MetricQueryID query_id)
 {
   int r = server.remove_osd_perf_query(query_id);
   if (r < 0) {
@@ -995,7 +1054,7 @@ void ActivePyModules::remove_osd_perf_query(OSDPerfMetricQueryID query_id)
   }
 }
 
-PyObject *ActivePyModules::get_osd_perf_counters(OSDPerfMetricQueryID query_id)
+PyObject *ActivePyModules::get_osd_perf_counters(MetricQueryID query_id)
 {
   std::map<OSDPerfMetricKey, PerformanceCounters> counters;
 
@@ -1042,9 +1101,45 @@ void ActivePyModules::cluster_log(const std::string &channel, clog_type prio,
 {
   std::lock_guard l(lock);
 
-  if (channel == "audit") {
-    audit_clog->do_log(prio, message);
-  } else {
-    clog->do_log(prio, message);
-  }
+  auto cl = monc.get_log_client()->create_channel(channel);
+  map<string,string> log_to_monitors;
+  map<string,string> log_to_syslog;
+  map<string,string> log_channel;
+  map<string,string> log_prio;
+  map<string,string> log_to_graylog;
+  map<string,string> log_to_graylog_host;
+  map<string,string> log_to_graylog_port;
+  uuid_d fsid;
+  string host;
+  if (parse_log_client_options(g_ceph_context, log_to_monitors, log_to_syslog,
+                              log_channel, log_prio, log_to_graylog,
+                              log_to_graylog_host, log_to_graylog_port,
+                              fsid, host) == 0)
+    cl->update_config(log_to_monitors, log_to_syslog,
+                     log_channel, log_prio, log_to_graylog,
+                     log_to_graylog_host, log_to_graylog_port,
+                     fsid, host);
+  cl->do_log(prio, message);
+}
+
+void ActivePyModules::register_client(std::string_view name, std::string addrs)
+{
+  std::lock_guard l(lock);
+
+  entity_addrvec_t addrv;
+  addrv.parse(addrs.data());
+
+  dout(7) << "registering msgr client handle " << addrv << dendl;
+  py_module_registry.register_client(name, std::move(addrv));
+}
+
+void ActivePyModules::unregister_client(std::string_view name, std::string addrs)
+{
+  std::lock_guard l(lock);
+
+  entity_addrvec_t addrv;
+  addrv.parse(addrs.data());
+
+  dout(7) << "unregistering msgr client handle " << addrv << dendl;
+  py_module_registry.unregister_client(name, addrv);
 }