]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/ActivePyModules.cc
import ceph quincy 17.2.1
[ceph.git] / ceph / src / mgr / ActivePyModules.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2014 John Spray <john.spray@inktank.com>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 */
13
14// Include this first to get python headers earlier
31f18b77 15#include "Gil.h"
7c673cae 16
20effc67
TL
17#include "ActivePyModules.h"
18
19#include <rocksdb/version.h>
20
7c673cae
FG
21#include "common/errno.h"
22#include "include/stringify.h"
23
7c673cae 24#include "mon/MonMap.h"
20effc67 25#include "osd/OSDMap.h"
a4b75251 26#include "osd/osd_types.h"
7c673cae 27#include "mgr/MgrContext.h"
20effc67
TL
28#include "mgr/TTLCache.h"
29#include "mgr/mgr_perf_counters.h"
7c673cae 30
20effc67
TL
31#include "DaemonKey.h"
32#include "DaemonServer.h"
33#include "mgr/MgrContext.h"
34#include "PyFormatter.h"
f67539c2 35// For ::mgr_store_prefix
11fdf7f2 36#include "PyModule.h"
3efd9988 37#include "PyModuleRegistry.h"
f67539c2 38#include "PyUtil.h"
3efd9988 39
7c673cae
FG
40#define dout_context g_ceph_context
41#define dout_subsys ceph_subsys_mgr
7c673cae 42#undef dout_prefix
9f95a23c 43#define dout_prefix *_dout << "mgr " << __func__ << " "
7c673cae 44
20effc67
TL
45using std::pair;
46using std::string;
47using namespace std::literals;
48
f67539c2
TL
49ActivePyModules::ActivePyModules(
50 PyModuleConfig &module_config_,
51 std::map<std::string, std::string> store_data,
52 bool mon_provides_kv_sub,
53 DaemonStateIndex &ds, ClusterState &cs,
54 MonClient &mc, LogChannelRef clog_,
55 LogChannelRef audit_clog_, Objecter &objecter_,
56 Client &client_, Finisher &f, DaemonServer &server,
57 PyModuleRegistry &pmr)
58: module_config(module_config_), daemon_state(ds), cluster_state(cs),
59 monc(mc), clog(clog_), audit_clog(audit_clog_), objecter(objecter_),
60 client(client_), finisher(f),
61 cmd_finisher(g_ceph_context, "cmd_finisher", "cmdfin"),
62 server(server), py_module_registry(pmr)
11fdf7f2
TL
63{
64 store_cache = std::move(store_data);
f67539c2
TL
65 // we can only trust our ConfigMap if the mon cluster has provided
66 // kv sub since our startup.
67 have_local_config_map = mon_provides_kv_sub;
68 _refresh_config_map();
81eedcae 69 cmd_finisher.start();
11fdf7f2 70}
7c673cae 71
3efd9988 72ActivePyModules::~ActivePyModules() = default;
7c673cae 73
3efd9988 74void ActivePyModules::dump_server(const std::string &hostname,
7c673cae
FG
75 const DaemonStateCollection &dmc,
76 Formatter *f)
77{
78 f->dump_string("hostname", hostname);
79 f->open_array_section("services");
80 std::string ceph_version;
81
9f95a23c 82 for (const auto &[key, state] : dmc) {
20effc67
TL
83 std::string id;
84 without_gil([&ceph_version, &id, state=state] {
f67539c2
TL
85 std::lock_guard l(state->lock);
86 // TODO: pick the highest version, and make sure that
87 // somewhere else (during health reporting?) we are
88 // indicating to the user if we see mixed versions
89 auto ver_iter = state->metadata.find("ceph_version");
90 if (ver_iter != state->metadata.end()) {
91 ceph_version = state->metadata.at("ceph_version");
92 }
20effc67
TL
93 if (state->metadata.find("id") != state->metadata.end()) {
94 id = state->metadata.at("id");
95 }
f67539c2 96 });
7c673cae 97 f->open_object_section("service");
9f95a23c
TL
98 f->dump_string("type", key.type);
99 f->dump_string("id", key.name);
33c7a0ef 100 f->dump_string("ceph_version", ceph_version);
20effc67
TL
101 if (!id.empty()) {
102 f->dump_string("name", id);
103 }
7c673cae
FG
104 f->close_section();
105 }
106 f->close_section();
107
108 f->dump_string("ceph_version", ceph_version);
109}
110
3efd9988 111PyObject *ActivePyModules::get_server_python(const std::string &hostname)
7c673cae 112{
f67539c2
TL
113 const auto dmc = without_gil([&]{
114 std::lock_guard l(lock);
115 dout(10) << " (" << hostname << ")" << dendl;
116 return daemon_state.get_by_server(hostname);
117 });
7c673cae
FG
118 PyFormatter f;
119 dump_server(hostname, dmc, &f);
120 return f.get();
121}
122
123
3efd9988 124PyObject *ActivePyModules::list_servers_python()
7c673cae 125{
7c673cae
FG
126 dout(10) << " >" << dendl;
127
f67539c2
TL
128 without_gil_t no_gil;
129 return daemon_state.with_daemons_by_server([this, &no_gil]
3efd9988 130 (const std::map<std::string, DaemonStateCollection> &all) {
20effc67 131 no_gil.acquire_gil();
f67539c2
TL
132 PyFormatter f(false, true);
133 for (const auto &[hostname, daemon_state] : all) {
3efd9988 134 f.open_object_section("server");
f67539c2 135 dump_server(hostname, daemon_state, &f);
3efd9988
FG
136 f.close_section();
137 }
f67539c2 138 return f.get();
3efd9988 139 });
7c673cae
FG
140}
141
3efd9988
FG
142PyObject *ActivePyModules::get_metadata_python(
143 const std::string &svc_type,
224ce89b 144 const std::string &svc_id)
7c673cae 145{
9f95a23c 146 auto metadata = daemon_state.get(DaemonKey{svc_type, svc_id});
3efd9988
FG
147 if (metadata == nullptr) {
148 derr << "Requested missing service " << svc_type << "." << svc_id << dendl;
149 Py_RETURN_NONE;
150 }
f67539c2
TL
151 auto l = without_gil([&] {
152 return std::lock_guard(lock);
153 });
7c673cae
FG
154 PyFormatter f;
155 f.dump_string("hostname", metadata->hostname);
f67539c2
TL
156 for (const auto &[key, val] : metadata->metadata) {
157 f.dump_string(key, val);
7c673cae
FG
158 }
159
160 return f.get();
161}
162
3efd9988
FG
163PyObject *ActivePyModules::get_daemon_status_python(
164 const std::string &svc_type,
224ce89b
WB
165 const std::string &svc_id)
166{
9f95a23c 167 auto metadata = daemon_state.get(DaemonKey{svc_type, svc_id});
3efd9988
FG
168 if (metadata == nullptr) {
169 derr << "Requested missing service " << svc_type << "." << svc_id << dendl;
170 Py_RETURN_NONE;
171 }
f67539c2
TL
172 auto l = without_gil([&] {
173 return std::lock_guard(lock);
174 });
224ce89b 175 PyFormatter f;
f67539c2
TL
176 for (const auto &[daemon, status] : metadata->service_status) {
177 f.dump_string(daemon, status);
224ce89b
WB
178 }
179 return f.get();
180}
7c673cae 181
20effc67
TL
182void ActivePyModules::update_cache_metrics() {
183 auto hit_miss_ratio = ttl_cache.get_hit_miss_ratio();
184 perfcounter->set(l_mgr_cache_hit, hit_miss_ratio.first);
185 perfcounter->set(l_mgr_cache_miss, hit_miss_ratio.second);
186}
187
188PyObject *ActivePyModules::cacheable_get_python(const std::string &what)
189{
190 uint64_t ttl_seconds = g_conf().get_val<uint64_t>("mgr_ttl_cache_expire_seconds");
191 if(ttl_seconds > 0) {
192 ttl_cache.set_ttl(ttl_seconds);
193 try{
194 PyObject* cached = ttl_cache.get(what);
195 update_cache_metrics();
196 return cached;
197 } catch (std::out_of_range& e) {}
198 }
199
200 PyObject *obj = get_python(what);
201 if(ttl_seconds && ttl_cache.is_cacheable(what)) {
202 ttl_cache.insert(what, obj);
203 Py_INCREF(obj);
204 }
205 update_cache_metrics();
206 return obj;
207}
208
3efd9988 209PyObject *ActivePyModules::get_python(const std::string &what)
7c673cae 210{
20effc67 211 uint64_t ttl_seconds = g_conf().get_val<uint64_t>("mgr_ttl_cache_expire_seconds");
11fdf7f2 212
20effc67
TL
213 PyFormatter pf;
214 PyJSONFormatter jf;
215 // Use PyJSONFormatter if TTL cache is enabled.
216 Formatter &f = ttl_seconds ? (Formatter&)jf : (Formatter&)pf;
7c673cae
FG
217
218 if (what == "fs_map") {
20effc67
TL
219 without_gil_t no_gil;
220 cluster_state.with_fsmap([&](const FSMap &fsmap) {
221 no_gil.acquire_gil();
7c673cae
FG
222 fsmap.dump(&f);
223 });
7c673cae 224 } else if (what == "osdmap_crush_map_text") {
20effc67 225 without_gil_t no_gil;
7c673cae 226 bufferlist rdata;
f67539c2 227 cluster_state.with_osdmap([&](const OSDMap &osd_map){
11fdf7f2 228 osd_map.crush->encode(rdata, CEPH_FEATURES_SUPPORTED_DEFAULT);
7c673cae
FG
229 });
230 std::string crush_text = rdata.to_str();
20effc67 231 no_gil.acquire_gil();
9f95a23c 232 return PyUnicode_FromString(crush_text.c_str());
7c673cae 233 } else if (what.substr(0, 7) == "osd_map") {
20effc67
TL
234 without_gil_t no_gil;
235 cluster_state.with_osdmap([&](const OSDMap &osd_map){
236 no_gil.acquire_gil();
7c673cae
FG
237 if (what == "osd_map") {
238 osd_map.dump(&f);
239 } else if (what == "osd_map_tree") {
240 osd_map.print_tree(&f, nullptr);
241 } else if (what == "osd_map_crush") {
242 osd_map.crush->dump(&f);
243 }
244 });
eafe8130 245 } else if (what == "modified_config_options") {
20effc67 246 without_gil_t no_gil;
eafe8130
TL
247 auto all_daemons = daemon_state.get_all();
248 set<string> names;
249 for (auto& [key, daemon] : all_daemons) {
250 std::lock_guard l(daemon->lock);
251 for (auto& [name, valmap] : daemon->config) {
252 names.insert(name);
253 }
254 }
20effc67 255 no_gil.acquire_gil();
eafe8130
TL
256 f.open_array_section("options");
257 for (auto& name : names) {
258 f.dump_string("name", name);
259 }
260 f.close_section();
1adf2230 261 } else if (what.substr(0, 6) == "config") {
1adf2230 262 if (what == "config_options") {
11fdf7f2 263 g_conf().config_options(&f);
1adf2230 264 } else if (what == "config") {
11fdf7f2 265 g_conf().show_config(&f);
1adf2230 266 }
7c673cae 267 } else if (what == "mon_map") {
20effc67
TL
268 without_gil_t no_gil;
269 cluster_state.with_monmap([&](const MonMap &monmap) {
270 no_gil.acquire_gil();
f67539c2 271 monmap.dump(&f);
f67539c2 272 });
224ce89b 273 } else if (what == "service_map") {
20effc67
TL
274 without_gil_t no_gil;
275 cluster_state.with_servicemap([&](const ServiceMap &service_map) {
276 no_gil.acquire_gil();
f67539c2 277 service_map.dump(&f);
f67539c2 278 });
7c673cae 279 } else if (what == "osd_metadata") {
20effc67 280 without_gil_t no_gil;
224ce89b 281 auto dmc = daemon_state.get_by_service("osd");
9f95a23c
TL
282 for (const auto &[key, state] : dmc) {
283 std::lock_guard l(state->lock);
f67539c2
TL
284 with_gil(no_gil, [&f, &name=key.name, state=state] {
285 f.open_object_section(name.c_str());
286 f.dump_string("hostname", state->hostname);
287 for (const auto &[name, val] : state->metadata) {
288 f.dump_string(name.c_str(), val);
289 }
290 f.close_section();
291 });
9f95a23c 292 }
9f95a23c 293 } else if (what == "mds_metadata") {
20effc67 294 without_gil_t no_gil;
9f95a23c 295 auto dmc = daemon_state.get_by_service("mds");
9f95a23c
TL
296 for (const auto &[key, state] : dmc) {
297 std::lock_guard l(state->lock);
f67539c2
TL
298 with_gil(no_gil, [&f, &name=key.name, state=state] {
299 f.open_object_section(name.c_str());
300 f.dump_string("hostname", state->hostname);
301 for (const auto &[name, val] : state->metadata) {
302 f.dump_string(name.c_str(), val);
303 }
304 f.close_section();
305 });
7c673cae 306 }
7c673cae 307 } else if (what == "pg_summary") {
20effc67
TL
308 without_gil_t no_gil;
309 cluster_state.with_pgmap(
f67539c2 310 [&f, &no_gil](const PGMap &pg_map) {
7c673cae
FG
311 std::map<std::string, std::map<std::string, uint32_t> > osds;
312 std::map<std::string, std::map<std::string, uint32_t> > pools;
313 std::map<std::string, uint32_t> all;
314 for (const auto &i : pg_map.pg_stat) {
315 const auto pool = i.first.m_pool;
316 const std::string state = pg_state_string(i.second.state);
317 // Insert to per-pool map
318 pools[stringify(pool)][state]++;
319 for (const auto &osd_id : i.second.acting) {
320 osds[stringify(osd_id)][state]++;
321 }
322 all[state]++;
323 }
20effc67 324 no_gil.acquire_gil();
7c673cae
FG
325 f.open_object_section("by_osd");
326 for (const auto &i : osds) {
327 f.open_object_section(i.first.c_str());
328 for (const auto &j : i.second) {
329 f.dump_int(j.first.c_str(), j.second);
330 }
331 f.close_section();
332 }
333 f.close_section();
334 f.open_object_section("by_pool");
335 for (const auto &i : pools) {
336 f.open_object_section(i.first.c_str());
337 for (const auto &j : i.second) {
338 f.dump_int(j.first.c_str(), j.second);
339 }
340 f.close_section();
341 }
342 f.close_section();
343 f.open_object_section("all");
344 for (const auto &i : all) {
345 f.dump_int(i.first.c_str(), i.second);
346 }
347 f.close_section();
28e407b8
AA
348 f.open_object_section("pg_stats_sum");
349 pg_map.pg_sum.dump(&f);
350 f.close_section();
7c673cae
FG
351 }
352 );
3efd9988 353 } else if (what == "pg_status") {
20effc67
TL
354 without_gil_t no_gil;
355 cluster_state.with_pgmap(
f67539c2 356 [&](const PGMap &pg_map) {
20effc67 357 no_gil.acquire_gil();
3efd9988
FG
358 pg_map.print_summary(&f, nullptr);
359 }
360 );
3efd9988 361 } else if (what == "pg_dump") {
20effc67
TL
362 without_gil_t no_gil;
363 cluster_state.with_pgmap(
f67539c2 364 [&](const PGMap &pg_map) {
20effc67 365 no_gil.acquire_gil();
9f95a23c 366 pg_map.dump(&f, false);
11fdf7f2
TL
367 }
368 );
11fdf7f2 369 } else if (what == "devices") {
20effc67 370 without_gil_t no_gil;
11fdf7f2 371 daemon_state.with_devices2(
f67539c2
TL
372 [&] {
373 with_gil(no_gil, [&] { f.open_array_section("devices"); });
11fdf7f2 374 },
f67539c2
TL
375 [&](const DeviceState &dev) {
376 with_gil(no_gil, [&] { f.dump_object("device", dev); });
11fdf7f2 377 });
20effc67 378 with_gil(no_gil, [&] {
f67539c2 379 f.close_section();
f67539c2 380 });
11fdf7f2
TL
381 } else if (what.size() > 7 &&
382 what.substr(0, 7) == "device ") {
20effc67 383 without_gil_t no_gil;
11fdf7f2 384 string devid = what.substr(7);
f67539c2
TL
385 if (!daemon_state.with_device(devid,
386 [&] (const DeviceState& dev) {
387 with_gil_t with_gil{no_gil};
388 f.dump_object("device", dev);
389 })) {
eafe8130 390 // device not found
eafe8130 391 }
11fdf7f2 392 } else if (what == "io_rate") {
20effc67
TL
393 without_gil_t no_gil;
394 cluster_state.with_pgmap(
f67539c2 395 [&](const PGMap &pg_map) {
20effc67 396 no_gil.acquire_gil();
11fdf7f2
TL
397 pg_map.dump_delta(&f);
398 }
3efd9988 399 );
7c673cae 400 } else if (what == "df") {
20effc67
TL
401 without_gil_t no_gil;
402 cluster_state.with_osdmap_and_pgmap(
f67539c2 403 [&](
11fdf7f2
TL
404 const OSDMap& osd_map,
405 const PGMap &pg_map) {
20effc67 406 no_gil.acquire_gil();
11fdf7f2 407 pg_map.dump_cluster_stats(nullptr, &f, true);
31f18b77 408 pg_map.dump_pool_stats_full(osd_map, nullptr, &f, true);
7c673cae 409 });
9f95a23c 410 } else if (what == "pg_stats") {
20effc67
TL
411 without_gil_t no_gil;
412 cluster_state.with_pgmap([&](const PGMap &pg_map) {
413 no_gil.acquire_gil();
9f95a23c
TL
414 pg_map.dump_pg_stats(&f, false);
415 });
9f95a23c 416 } else if (what == "pool_stats") {
20effc67
TL
417 without_gil_t no_gil;
418 cluster_state.with_pgmap([&](const PGMap &pg_map) {
419 no_gil.acquire_gil();
9f95a23c
TL
420 pg_map.dump_pool_stats(&f);
421 });
9f95a23c 422 } else if (what == "pg_ready") {
9f95a23c 423 server.dump_pg_ready(&f);
20effc67
TL
424 } else if (what == "pg_progress") {
425 without_gil_t no_gil;
426 cluster_state.with_pgmap([&](const PGMap &pg_map) {
427 no_gil.acquire_gil();
428 pg_map.dump_pg_progress(&f);
429 server.dump_pg_ready(&f);
430 });
7c673cae 431 } else if (what == "osd_stats") {
20effc67
TL
432 without_gil_t no_gil;
433 cluster_state.with_pgmap([&](const PGMap &pg_map) {
434 no_gil.acquire_gil();
ded94939 435 pg_map.dump_osd_stats(&f, false);
7c673cae 436 });
9f95a23c 437 } else if (what == "osd_ping_times") {
20effc67
TL
438 without_gil_t no_gil;
439 cluster_state.with_pgmap([&](const PGMap &pg_map) {
440 no_gil.acquire_gil();
9f95a23c
TL
441 pg_map.dump_osd_ping_times(&f);
442 });
11fdf7f2 443 } else if (what == "osd_pool_stats") {
20effc67 444 without_gil_t no_gil;
11fdf7f2 445 int64_t poolid = -ENOENT;
20effc67 446 cluster_state.with_osdmap_and_pgmap([&](const OSDMap& osdmap,
11fdf7f2 447 const PGMap& pg_map) {
20effc67 448 no_gil.acquire_gil();
f67539c2
TL
449 f.open_array_section("pool_stats");
450 for (auto &p : osdmap.get_pools()) {
451 poolid = p.first;
452 pg_map.dump_pool_stats_and_io_rate(poolid, osdmap, &f, nullptr);
453 }
454 f.close_section();
11fdf7f2 455 });
e306af50 456 } else if (what == "health") {
20effc67
TL
457 without_gil_t no_gil;
458 cluster_state.with_health([&](const ceph::bufferlist &health_json) {
459 no_gil.acquire_gil();
e306af50
TL
460 f.dump_string("json", health_json.to_str());
461 });
e306af50 462 } else if (what == "mon_status") {
20effc67
TL
463 without_gil_t no_gil;
464 cluster_state.with_mon_status(
f67539c2 465 [&](const ceph::bufferlist &mon_status_json) {
20effc67 466 no_gil.acquire_gil();
e306af50
TL
467 f.dump_string("json", mon_status_json.to_str());
468 });
224ce89b 469 } else if (what == "mgr_map") {
20effc67
TL
470 without_gil_t no_gil;
471 cluster_state.with_mgrmap([&](const MgrMap &mgr_map) {
472 no_gil.acquire_gil();
224ce89b
WB
473 mgr_map.dump(&f);
474 });
b3b6e05e
TL
475 } else if (what == "mgr_ips") {
476 entity_addrvec_t myaddrs = server.get_myaddrs();
b3b6e05e
TL
477 f.open_array_section("ips");
478 std::set<std::string> did;
479 for (auto& i : myaddrs.v) {
480 std::string ip = i.ip_only_to_str();
481 if (auto [where, inserted] = did.insert(ip); inserted) {
482 f.dump_string("ip", ip);
483 }
484 }
485 f.close_section();
f67539c2 486 } else if (what == "have_local_config_map") {
f67539c2 487 f.dump_bool("have_local_config_map", have_local_config_map);
a4b75251 488 } else if (what == "active_clean_pgs"){
20effc67 489 without_gil_t no_gil;
a4b75251
TL
490 cluster_state.with_pgmap(
491 [&](const PGMap &pg_map) {
20effc67 492 no_gil.acquire_gil();
a4b75251
TL
493 f.open_array_section("pg_stats");
494 for (auto &i : pg_map.pg_stat) {
495 const auto state = i.second.state;
496 const auto pgid_raw = i.first;
497 const auto pgid = stringify(pgid_raw.m_pool) + "." + stringify(pgid_raw.m_seed);
498 const auto reported_epoch = i.second.reported_epoch;
499 if (state & PG_STATE_ACTIVE && state & PG_STATE_CLEAN) {
500 f.open_object_section("pg_stat");
501 f.dump_string("pgid", pgid);
502 f.dump_string("state", pg_state_string(state));
503 f.dump_unsigned("reported_epoch", reported_epoch);
504 f.close_section();
505 }
506 }
507 f.close_section();
508 const auto num_pg = pg_map.num_pg;
509 f.dump_unsigned("total_num_pgs", num_pg);
510 });
7c673cae
FG
511 } else {
512 derr << "Python module requested unknown data '" << what << "'" << dendl;
513 Py_RETURN_NONE;
514 }
20effc67
TL
515 without_gil_t no_gil;
516 no_gil.acquire_gil();
517 if(ttl_seconds) {
518 return jf.get();
519 } else {
520 return pf.get();
521 }
7c673cae
FG
522}
523
11fdf7f2 524void ActivePyModules::start_one(PyModuleRef py_module)
7c673cae 525{
11fdf7f2
TL
526 std::lock_guard l(lock);
527
11fdf7f2 528 const auto name = py_module->get_name();
f6b5b4d7 529 auto active_module = std::make_shared<ActivePyModule>(py_module, clog);
11fdf7f2 530
f6b5b4d7 531 pending_modules.insert(name);
11fdf7f2
TL
532 // Send all python calls down a Finisher to avoid blocking
533 // C++ code, and avoid any potential lock cycles.
9f95a23c 534 finisher.queue(new LambdaContext([this, active_module, name](int) {
11fdf7f2 535 int r = active_module->load(this);
f6b5b4d7
TL
536 std::lock_guard l(lock);
537 pending_modules.erase(name);
11fdf7f2
TL
538 if (r != 0) {
539 derr << "Failed to run module in active mode ('" << name << "')"
540 << dendl;
11fdf7f2 541 } else {
f6b5b4d7
TL
542 auto em = modules.emplace(name, active_module);
543 ceph_assert(em.second); // actually inserted
544
11fdf7f2
TL
545 dout(4) << "Starting thread for " << name << dendl;
546 active_module->thread.create(active_module->get_thread_name());
547 }
548 }));
7c673cae
FG
549}
550
3efd9988 551void ActivePyModules::shutdown()
7c673cae 552{
11fdf7f2 553 std::lock_guard locker(lock);
7c673cae
FG
554
555 // Signal modules to drop out of serve() and/or tear down resources
9f95a23c
TL
556 for (auto& [name, module] : modules) {
557 lock.unlock();
3efd9988 558 dout(10) << "calling module " << name << " shutdown()" << dendl;
7c673cae 559 module->shutdown();
3efd9988 560 dout(10) << "module " << name << " shutdown() returned" << dendl;
9f95a23c 561 lock.lock();
7c673cae
FG
562 }
563
564 // For modules implementing serve(), finish the threads where we
565 // were running that.
9f95a23c
TL
566 for (auto& [name, module] : modules) {
567 lock.unlock();
568 dout(10) << "joining module " << name << dendl;
569 module->thread.join();
570 dout(10) << "joined module " << name << dendl;
571 lock.lock();
7c673cae 572 }
7c673cae 573
81eedcae
TL
574 cmd_finisher.wait_for_empty();
575 cmd_finisher.stop();
576
7c673cae 577 modules.clear();
7c673cae
FG
578}
579
3efd9988 580void ActivePyModules::notify_all(const std::string &notify_type,
7c673cae
FG
581 const std::string &notify_id)
582{
11fdf7f2 583 std::lock_guard l(lock);
7c673cae
FG
584
585 dout(10) << __func__ << ": notify_all " << notify_type << dendl;
9f95a23c 586 for (auto& [name, module] : modules) {
20effc67
TL
587 if (!py_module_registry.should_notify(name, notify_type)) {
588 continue;
589 }
7c673cae
FG
590 // Send all python calls down a Finisher to avoid blocking
591 // C++ code, and avoid any potential lock cycles.
20effc67 592 dout(15) << "queuing notify (" << notify_type << ") to " << name << dendl;
9f95a23c
TL
593 // workaround for https://bugs.llvm.org/show_bug.cgi?id=35984
594 finisher.queue(new LambdaContext([module=module, notify_type, notify_id]
adb31ebb
TL
595 (int r){
596 module->notify(notify_type, notify_id);
7c673cae
FG
597 }));
598 }
599}
600
3efd9988 601void ActivePyModules::notify_all(const LogEntry &log_entry)
7c673cae 602{
11fdf7f2 603 std::lock_guard l(lock);
7c673cae
FG
604
605 dout(10) << __func__ << ": notify_all (clog)" << dendl;
9f95a23c 606 for (auto& [name, module] : modules) {
20effc67
TL
607 if (!py_module_registry.should_notify(name, "clog")) {
608 continue;
609 }
7c673cae
FG
610 // Send all python calls down a Finisher to avoid blocking
611 // C++ code, and avoid any potential lock cycles.
612 //
613 // Note intentional use of non-reference lambda binding on
614 // log_entry: we take a copy because caller's instance is
615 // probably ephemeral.
9f95a23c
TL
616 dout(15) << "queuing notify (clog) to " << name << dendl;
617 // workaround for https://bugs.llvm.org/show_bug.cgi?id=35984
618 finisher.queue(new LambdaContext([module=module, log_entry](int r){
7c673cae
FG
619 module->notify_clog(log_entry);
620 }));
621 }
622}
623
11fdf7f2 624bool ActivePyModules::get_store(const std::string &module_name,
7c673cae
FG
625 const std::string &key, std::string *val) const
626{
f67539c2 627 without_gil_t no_gil;
11fdf7f2 628 std::lock_guard l(lock);
11fdf7f2 629
f67539c2 630 const std::string global_key = PyModule::mgr_store_prefix
3efd9988 631 + module_name + "/" + key;
31f18b77 632
11fdf7f2 633 dout(4) << __func__ << " key: " << global_key << dendl;
7c673cae 634
11fdf7f2
TL
635 auto i = store_cache.find(global_key);
636 if (i != store_cache.end()) {
637 *val = i->second;
638 return true;
639 } else {
640 return false;
641 }
642}
643
644PyObject *ActivePyModules::dispatch_remote(
645 const std::string &other_module,
646 const std::string &method,
647 PyObject *args,
648 PyObject *kwargs,
649 std::string *err)
650{
651 auto mod_iter = modules.find(other_module);
652 ceph_assert(mod_iter != modules.end());
653
654 return mod_iter->second->dispatch_remote(method, args, kwargs, err);
655}
a8e16298 656
11fdf7f2
TL
657bool ActivePyModules::get_config(const std::string &module_name,
658 const std::string &key, std::string *val) const
659{
f67539c2 660 const std::string global_key = "mgr/" + module_name + "/" + key;
11fdf7f2 661
9f95a23c 662 dout(20) << " key: " << global_key << dendl;
11fdf7f2
TL
663
664 std::lock_guard lock(module_config.lock);
9f95a23c 665
11fdf7f2
TL
666 auto i = module_config.config.find(global_key);
667 if (i != module_config.config.end()) {
668 *val = i->second;
7c673cae
FG
669 return true;
670 } else {
671 return false;
672 }
673}
674
11fdf7f2
TL
675PyObject *ActivePyModules::get_typed_config(
676 const std::string &module_name,
677 const std::string &key,
678 const std::string &prefix) const
679{
f67539c2 680 without_gil_t no_gil;
11fdf7f2
TL
681 std::string value;
682 std::string final_key;
683 bool found = false;
684 if (prefix.size()) {
685 final_key = prefix + "/" + key;
686 found = get_config(module_name, final_key, &value);
687 }
688 if (!found) {
689 final_key = key;
690 found = get_config(module_name, final_key, &value);
691 }
692 if (found) {
693 PyModuleRef module = py_module_registry.get_module(module_name);
20effc67 694 no_gil.acquire_gil();
11fdf7f2
TL
695 if (!module) {
696 derr << "Module '" << module_name << "' is not available" << dendl;
697 Py_RETURN_NONE;
698 }
adb31ebb
TL
699 // removing value to hide sensitive data going into mgr logs
700 // leaving this for debugging purposes
701 // dout(10) << __func__ << " " << final_key << " found: " << value << dendl;
702 dout(10) << __func__ << " " << final_key << " found" << dendl;
11fdf7f2
TL
703 return module->get_typed_option_value(key, value);
704 }
11fdf7f2 705 if (prefix.size()) {
9f95a23c 706 dout(10) << " [" << prefix << "/]" << key << " not found "
11fdf7f2
TL
707 << dendl;
708 } else {
9f95a23c 709 dout(10) << " " << key << " not found " << dendl;
11fdf7f2
TL
710 }
711 Py_RETURN_NONE;
712}
713
714PyObject *ActivePyModules::get_store_prefix(const std::string &module_name,
31f18b77
FG
715 const std::string &prefix) const
716{
f67539c2 717 without_gil_t no_gil;
11fdf7f2
TL
718 std::lock_guard l(lock);
719 std::lock_guard lock(module_config.lock);
20effc67 720 no_gil.acquire_gil();
31f18b77 721
f67539c2 722 const std::string base_prefix = PyModule::mgr_store_prefix
3efd9988 723 + module_name + "/";
31f18b77 724 const std::string global_prefix = base_prefix + prefix;
11fdf7f2 725 dout(4) << __func__ << " prefix: " << global_prefix << dendl;
31f18b77 726
20effc67
TL
727 PyFormatter f;
728 for (auto p = store_cache.lower_bound(global_prefix);
729 p != store_cache.end() && p->first.find(global_prefix) == 0; ++p) {
730 f.dump_string(p->first.c_str() + base_prefix.size(), p->second);
731 }
732 return f.get();
31f18b77
FG
733}
734
11fdf7f2 735void ActivePyModules::set_store(const std::string &module_name,
20effc67 736 const std::string &key, const std::optional<std::string>& val)
7c673cae 737{
f67539c2 738 const std::string global_key = PyModule::mgr_store_prefix
3efd9988 739 + module_name + "/" + key;
9f95a23c 740
7c673cae
FG
741 Command set_cmd;
742 {
11fdf7f2 743 std::lock_guard l(lock);
f67539c2
TL
744
745 // NOTE: this isn't strictly necessary since we'll also get an MKVData
746 // update from the mon due to our subscription *before* our command is acked.
d2e6a577 747 if (val) {
11fdf7f2 748 store_cache[global_key] = *val;
d2e6a577 749 } else {
11fdf7f2 750 store_cache.erase(global_key);
d2e6a577 751 }
7c673cae
FG
752
753 std::ostringstream cmd_json;
7c673cae
FG
754 JSONFormatter jf;
755 jf.open_object_section("cmd");
d2e6a577
FG
756 if (val) {
757 jf.dump_string("prefix", "config-key set");
758 jf.dump_string("key", global_key);
759 jf.dump_string("val", *val);
760 } else {
761 jf.dump_string("prefix", "config-key del");
762 jf.dump_string("key", global_key);
763 }
7c673cae
FG
764 jf.close_section();
765 jf.flush(cmd_json);
7c673cae
FG
766 set_cmd.run(&monc, cmd_json.str());
767 }
768 set_cmd.wait();
769
770 if (set_cmd.r != 0) {
c07f9fc5 771 // config-key set will fail if mgr's auth key has insufficient
7c673cae
FG
772 // permission to set config keys
773 // FIXME: should this somehow raise an exception back into Python land?
c07f9fc5 774 dout(0) << "`config-key set " << global_key << " " << val << "` failed: "
7c673cae
FG
775 << cpp_strerror(set_cmd.r) << dendl;
776 dout(0) << "mon returned " << set_cmd.r << ": " << set_cmd.outs << dendl;
777 }
778}
779
20effc67
TL
780std::pair<int, std::string> ActivePyModules::set_config(
781 const std::string &module_name,
782 const std::string &key,
783 const std::optional<std::string>& val)
c07f9fc5 784{
20effc67 785 return module_config.set_config(&monc, module_name, key, val);
c07f9fc5
FG
786}
787
3efd9988 788std::map<std::string, std::string> ActivePyModules::get_services() const
7c673cae 789{
3efd9988 790 std::map<std::string, std::string> result;
11fdf7f2 791 std::lock_guard l(lock);
9f95a23c 792 for (const auto& [name, module] : modules) {
3efd9988
FG
793 std::string svc_str = module->get_uri();
794 if (!svc_str.empty()) {
9f95a23c 795 result[name] = svc_str;
3efd9988
FG
796 }
797 }
7c673cae 798
3efd9988 799 return result;
7c673cae
FG
800}
801
f67539c2
TL
802void ActivePyModules::update_kv_data(
803 const std::string prefix,
804 bool incremental,
20effc67 805 const map<std::string, std::optional<bufferlist>, std::less<>>& data)
f67539c2
TL
806{
807 std::lock_guard l(lock);
808 bool do_config = false;
809 if (!incremental) {
810 dout(10) << "full update on " << prefix << dendl;
811 auto p = store_cache.lower_bound(prefix);
812 while (p != store_cache.end() && p->first.find(prefix) == 0) {
813 dout(20) << " rm prior " << p->first << dendl;
814 p = store_cache.erase(p);
815 }
816 } else {
817 dout(10) << "incremental update on " << prefix << dendl;
818 }
819 for (auto& i : data) {
820 if (i.second) {
821 dout(20) << " set " << i.first << " = " << i.second->to_str() << dendl;
822 store_cache[i.first] = i.second->to_str();
823 } else {
824 dout(20) << " rm " << i.first << dendl;
825 store_cache.erase(i.first);
826 }
827 if (i.first.find("config/") == 0) {
828 do_config = true;
829 }
830 }
831 if (do_config) {
832 _refresh_config_map();
833 }
834}
835
836void ActivePyModules::_refresh_config_map()
837{
838 dout(10) << dendl;
839 config_map.clear();
840 for (auto p = store_cache.lower_bound("config/");
841 p != store_cache.end() && p->first.find("config/") == 0;
842 ++p) {
843 string key = p->first.substr(7);
844 if (key.find("mgr/") == 0) {
845 // NOTE: for now, we ignore module options. see also ceph_foreign_option_get().
846 continue;
847 }
848 string value = p->second;
849 string name;
850 string who;
851 config_map.parse_key(key, &name, &who);
852
853 const Option *opt = g_conf().find_option(name);
854 if (!opt) {
855 config_map.stray_options.push_back(
856 std::unique_ptr<Option>(
857 new Option(name, Option::TYPE_STR, Option::LEVEL_UNKNOWN)));
858 opt = config_map.stray_options.back().get();
859 }
860
861 string err;
862 int r = opt->pre_validate(&value, &err);
863 if (r < 0) {
864 dout(10) << __func__ << " pre-validate failed on '" << name << "' = '"
865 << value << "' for " << name << dendl;
866 }
867
868 MaskedOption mopt(opt);
869 mopt.raw_value = value;
870 string section_name;
871 if (who.size() &&
872 !ConfigMap::parse_mask(who, &section_name, &mopt.mask)) {
873 derr << __func__ << " invalid mask for key " << key << dendl;
874 } else if (opt->has_flag(Option::FLAG_NO_MON_UPDATE)) {
875 dout(10) << __func__ << " NO_MON_UPDATE option '"
876 << name << "' = '" << value << "' for " << name
877 << dendl;
878 } else {
879 Section *section = &config_map.global;;
880 if (section_name.size() && section_name != "global") {
881 if (section_name.find('.') != std::string::npos) {
882 section = &config_map.by_id[section_name];
883 } else {
884 section = &config_map.by_type[section_name];
885 }
886 }
887 section->options.insert(make_pair(name, std::move(mopt)));
888 }
889 }
890}
891
11fdf7f2
TL
892PyObject* ActivePyModules::with_perf_counters(
893 std::function<void(PerfCounterInstance& counter_instance, PerfCounterType& counter_type, PyFormatter& f)> fct,
224ce89b 894 const std::string &svc_name,
7c673cae 895 const std::string &svc_id,
11fdf7f2 896 const std::string &path) const
7c673cae 897{
7c673cae 898 PyFormatter f;
f67539c2
TL
899 f.open_array_section(path);
900 {
901 without_gil_t no_gil;
902 std::lock_guard l(lock);
903 auto metadata = daemon_state.get(DaemonKey{svc_name, svc_id});
904 if (metadata) {
905 std::lock_guard l2(metadata->lock);
906 if (metadata->perf_counters.instances.count(path)) {
907 auto counter_instance = metadata->perf_counters.instances.at(path);
908 auto counter_type = metadata->perf_counters.types.at(path);
909 with_gil(no_gil, [&] {
910 fct(counter_instance, counter_type, f);
911 });
912 } else {
913 dout(4) << "Missing counter: '" << path << "' ("
914 << svc_name << "." << svc_id << ")" << dendl;
915 dout(20) << "Paths are:" << dendl;
916 for (const auto &i : metadata->perf_counters.instances) {
917 dout(20) << i.first << dendl;
918 }
7c673cae 919 }
f67539c2
TL
920 } else {
921 dout(4) << "No daemon state for " << svc_name << "." << svc_id << ")"
922 << dendl;
7c673cae 923 }
7c673cae
FG
924 }
925 f.close_section();
926 return f.get();
927}
928
11fdf7f2
TL
929PyObject* ActivePyModules::get_counter_python(
930 const std::string &svc_name,
931 const std::string &svc_id,
932 const std::string &path)
933{
934 auto extract_counters = [](
935 PerfCounterInstance& counter_instance,
936 PerfCounterType& counter_type,
937 PyFormatter& f)
938 {
939 if (counter_type.type & PERFCOUNTER_LONGRUNAVG) {
940 const auto &avg_data = counter_instance.get_data_avg();
941 for (const auto &datapoint : avg_data) {
942 f.open_array_section("datapoint");
9f95a23c 943 f.dump_float("t", datapoint.t);
11fdf7f2
TL
944 f.dump_unsigned("s", datapoint.s);
945 f.dump_unsigned("c", datapoint.c);
946 f.close_section();
947 }
948 } else {
949 const auto &data = counter_instance.get_data();
950 for (const auto &datapoint : data) {
951 f.open_array_section("datapoint");
9f95a23c 952 f.dump_float("t", datapoint.t);
11fdf7f2
TL
953 f.dump_unsigned("v", datapoint.v);
954 f.close_section();
955 }
956 }
957 };
958 return with_perf_counters(extract_counters, svc_name, svc_id, path);
959}
960
961PyObject* ActivePyModules::get_latest_counter_python(
962 const std::string &svc_name,
963 const std::string &svc_id,
964 const std::string &path)
965{
966 auto extract_latest_counters = [](
967 PerfCounterInstance& counter_instance,
968 PerfCounterType& counter_type,
969 PyFormatter& f)
970 {
971 if (counter_type.type & PERFCOUNTER_LONGRUNAVG) {
972 const auto &datapoint = counter_instance.get_latest_data_avg();
9f95a23c 973 f.dump_float("t", datapoint.t);
11fdf7f2
TL
974 f.dump_unsigned("s", datapoint.s);
975 f.dump_unsigned("c", datapoint.c);
976 } else {
977 const auto &datapoint = counter_instance.get_latest_data();
9f95a23c 978 f.dump_float("t", datapoint.t);
11fdf7f2
TL
979 f.dump_unsigned("v", datapoint.v);
980 }
981 };
982 return with_perf_counters(extract_latest_counters, svc_name, svc_id, path);
983}
984
3efd9988 985PyObject* ActivePyModules::get_perf_schema_python(
11fdf7f2 986 const std::string &svc_type,
c07f9fc5
FG
987 const std::string &svc_id)
988{
f67539c2 989 without_gil_t no_gil;
11fdf7f2 990 std::lock_guard l(lock);
c07f9fc5 991
3efd9988 992 DaemonStateCollection daemons;
c07f9fc5
FG
993
994 if (svc_type == "") {
11fdf7f2 995 daemons = daemon_state.get_all();
c07f9fc5 996 } else if (svc_id.empty()) {
11fdf7f2 997 daemons = daemon_state.get_by_service(svc_type);
c07f9fc5 998 } else {
9f95a23c 999 auto key = DaemonKey{svc_type, svc_id};
c07f9fc5 1000 // so that the below can be a loop in all cases
3efd9988
FG
1001 auto got = daemon_state.get(key);
1002 if (got != nullptr) {
1003 daemons[key] = got;
c07f9fc5
FG
1004 }
1005 }
1006
f67539c2
TL
1007 auto f = with_gil(no_gil, [&] {
1008 return PyFormatter();
1009 });
3efd9988 1010 if (!daemons.empty()) {
9f95a23c 1011 for (auto& [key, state] : daemons) {
11fdf7f2 1012 std::lock_guard l(state->lock);
f67539c2
TL
1013 with_gil(no_gil, [&, key=ceph::to_string(key), state=state] {
1014 f.open_object_section(key.c_str());
1015 for (auto ctr_inst_iter : state->perf_counters.instances) {
1016 const auto &counter_name = ctr_inst_iter.first;
1017 f.open_object_section(counter_name.c_str());
1018 auto type = state->perf_counters.types[counter_name];
1019 f.dump_string("description", type.description);
1020 if (!type.nick.empty()) {
1021 f.dump_string("nick", type.nick);
1022 }
1023 f.dump_unsigned("type", type.type);
1024 f.dump_unsigned("priority", type.priority);
1025 f.dump_unsigned("units", type.unit);
1026 f.close_section();
1027 }
1028 f.close_section();
1029 });
c07f9fc5
FG
1030 }
1031 } else {
1032 dout(4) << __func__ << ": No daemon state found for "
1033 << svc_type << "." << svc_id << ")" << dendl;
1034 }
c07f9fc5
FG
1035 return f.get();
1036}
1037
20effc67
TL
1038PyObject* ActivePyModules::get_rocksdb_version()
1039{
1040 std::string version = std::to_string(ROCKSDB_MAJOR) + "." +
1041 std::to_string(ROCKSDB_MINOR) + "." +
1042 std::to_string(ROCKSDB_PATCH);
1043
1044 return PyUnicode_FromString(version.c_str());
1045}
1046
3efd9988 1047PyObject *ActivePyModules::get_context()
7c673cae 1048{
f67539c2
TL
1049 auto l = without_gil([&] {
1050 return std::lock_guard(lock);
1051 });
7c673cae
FG
1052 // Construct a capsule containing ceph context.
1053 // Not incrementing/decrementing ref count on the context because
1054 // it's the global one and it has process lifetime.
1055 auto capsule = PyCapsule_New(g_ceph_context, nullptr, nullptr);
1056 return capsule;
1057}
1058
3efd9988
FG
1059/**
1060 * Helper for our wrapped types that take a capsule in their constructor.
1061 */
1062PyObject *construct_with_capsule(
1063 const std::string &module_name,
1064 const std::string &clsname,
1065 void *wrapped)
224ce89b 1066{
3efd9988
FG
1067 // Look up the OSDMap type which we will construct
1068 PyObject *module = PyImport_ImportModule(module_name.c_str());
1069 if (!module) {
1070 derr << "Failed to import python module:" << dendl;
20effc67
TL
1071 derr << handle_pyerror(true, module_name,
1072 "construct_with_capsule "s + module_name + " " + clsname) << dendl;
224ce89b 1073 }
11fdf7f2 1074 ceph_assert(module);
3efd9988
FG
1075
1076 PyObject *wrapper_type = PyObject_GetAttrString(
1077 module, (const char*)clsname.c_str());
1078 if (!wrapper_type) {
1079 derr << "Failed to get python type:" << dendl;
20effc67
TL
1080 derr << handle_pyerror(true, module_name,
1081 "construct_with_capsule "s + module_name + " " + clsname) << dendl;
224ce89b 1082 }
11fdf7f2 1083 ceph_assert(wrapper_type);
3efd9988
FG
1084
1085 // Construct a capsule containing an OSDMap.
1086 auto wrapped_capsule = PyCapsule_New(wrapped, nullptr, nullptr);
11fdf7f2 1087 ceph_assert(wrapped_capsule);
3efd9988
FG
1088
1089 // Construct the python OSDMap
1090 auto pArgs = PyTuple_Pack(1, wrapped_capsule);
1091 auto wrapper_instance = PyObject_CallObject(wrapper_type, pArgs);
1092 if (wrapper_instance == nullptr) {
1093 derr << "Failed to construct python OSDMap:" << dendl;
20effc67
TL
1094 derr << handle_pyerror(true, module_name,
1095 "construct_with_capsule "s + module_name + " " + clsname) << dendl;
3efd9988 1096 }
11fdf7f2 1097 ceph_assert(wrapper_instance != nullptr);
3efd9988
FG
1098 Py_DECREF(pArgs);
1099 Py_DECREF(wrapped_capsule);
1100
1101 Py_DECREF(wrapper_type);
1102 Py_DECREF(module);
1103
1104 return wrapper_instance;
224ce89b
WB
1105}
1106
3efd9988 1107PyObject *ActivePyModules::get_osdmap()
224ce89b 1108{
f67539c2
TL
1109 auto newmap = without_gil([&] {
1110 OSDMap *newmap = new OSDMap;
11fdf7f2 1111 cluster_state.with_osdmap([&](const OSDMap& o) {
f67539c2
TL
1112 newmap->deepish_copy_from(o);
1113 });
1114 return newmap;
1115 });
1116 return construct_with_capsule("mgr_module", "OSDMap", (void*)newmap);
1117}
1118
1119PyObject *ActivePyModules::get_foreign_config(
1120 const std::string& who,
1121 const std::string& name)
1122{
1123 dout(10) << "ceph_foreign_option_get " << who << " " << name << dendl;
1124
1125 // NOTE: for now this will only work with build-in options, not module options.
1126 const Option *opt = g_conf().find_option(name);
1127 if (!opt) {
1128 dout(4) << "ceph_foreign_option_get " << name << " not found " << dendl;
1129 PyErr_Format(PyExc_KeyError, "option not found: %s", name.c_str());
1130 return nullptr;
11fdf7f2 1131 }
3efd9988 1132
f67539c2
TL
1133 // If the monitors are not yet running pacific, we cannot rely on our local
1134 // ConfigMap
1135 if (!have_local_config_map) {
1136 dout(20) << "mon cluster wasn't pacific when we started: falling back to 'config get'"
1137 << dendl;
1138 without_gil_t no_gil;
1139 Command cmd;
1140 {
1141 std::lock_guard l(lock);
1142 cmd.run(
1143 &monc,
1144 "{\"prefix\": \"config get\","s +
1145 "\"who\": \""s + who + "\","s +
1146 "\"key\": \""s + name + "\"}");
1147 }
1148 cmd.wait();
1149 dout(10) << "ceph_foreign_option_get (mon command) " << who << " " << name << " = "
1150 << cmd.outbl.to_str() << dendl;
20effc67 1151 no_gil.acquire_gil();
f67539c2
TL
1152 return get_python_typed_option_value(opt->type, cmd.outbl.to_str());
1153 }
1154
1155 // mimic the behavor of mon/ConfigMonitor's 'config get' command
1156 EntityName entity;
1157 if (!entity.from_str(who) &&
1158 !entity.from_str(who + ".")) {
1159 dout(5) << "unrecognized entity '" << who << "'" << dendl;
1160 PyErr_Format(PyExc_KeyError, "invalid entity: %s", who.c_str());
1161 return nullptr;
1162 }
1163
1164 without_gil_t no_gil;
1165 lock.lock();
1166
1167 // FIXME: this is super inefficient, since we generate the entire daemon
1168 // config just to extract one value from it!
1169
1170 std::map<std::string,std::string,std::less<>> config;
1171 cluster_state.with_osdmap([&](const OSDMap &osdmap) {
1172 map<string,string> crush_location;
1173 string device_class;
1174 if (entity.is_osd()) {
1175 osdmap.crush->get_full_location(who, &crush_location);
1176 int id = atoi(entity.get_id().c_str());
1177 const char *c = osdmap.crush->get_item_class(id);
1178 if (c) {
1179 device_class = c;
1180 }
1181 dout(10) << __func__ << " crush_location " << crush_location
1182 << " class " << device_class << dendl;
1183 }
1184
1185 std::map<std::string,pair<std::string,const MaskedOption*>> src;
1186 config = config_map.generate_entity_map(
1187 entity,
1188 crush_location,
1189 osdmap.crush.get(),
1190 device_class,
1191 &src);
1192 });
1193
1194 // get a single value
1195 string value;
1196 auto p = config.find(name);
1197 if (p != config.end()) {
1198 value = p->second;
1199 } else {
1200 if (!entity.is_client() &&
20effc67 1201 opt->daemon_value != Option::value_t{}) {
f67539c2
TL
1202 value = Option::to_str(opt->daemon_value);
1203 } else {
1204 value = Option::to_str(opt->value);
1205 }
1206 }
1207
1208 dout(10) << "ceph_foreign_option_get (configmap) " << who << " " << name << " = "
1209 << value << dendl;
1210 lock.unlock();
20effc67 1211 no_gil.acquire_gil();
f67539c2 1212 return get_python_typed_option_value(opt->type, value);
224ce89b 1213}
c07f9fc5 1214
3efd9988 1215void ActivePyModules::set_health_checks(const std::string& module_name,
c07f9fc5
FG
1216 health_check_map_t&& checks)
1217{
11fdf7f2
TL
1218 bool changed = false;
1219
9f95a23c 1220 lock.lock();
3efd9988 1221 auto p = modules.find(module_name);
c07f9fc5 1222 if (p != modules.end()) {
11fdf7f2 1223 changed = p->second->set_health_checks(std::move(checks));
c07f9fc5 1224 }
9f95a23c 1225 lock.unlock();
11fdf7f2
TL
1226
1227 // immediately schedule a report to be sent to the monitors with the new
1228 // health checks that have changed. This is done asynchronusly to avoid
1229 // blocking python land. ActivePyModules::lock needs to be dropped to make
1230 // lockdep happy:
1231 //
1232 // send_report callers: DaemonServer::lock -> PyModuleRegistery::lock
1233 // active_start: PyModuleRegistry::lock -> ActivePyModules::lock
1234 //
1235 // if we don't release this->lock before calling schedule_tick a cycle is
1236 // formed with the addition of ActivePyModules::lock -> DaemonServer::lock.
1237 // This is still correct as send_report is run asynchronously under
1238 // DaemonServer::lock.
1239 if (changed)
1240 server.schedule_tick(0);
1241}
1242
1243int ActivePyModules::handle_command(
92f5a8d4
TL
1244 const ModuleCommand& module_command,
1245 const MgrSession& session,
11fdf7f2
TL
1246 const cmdmap_t &cmdmap,
1247 const bufferlist &inbuf,
1248 std::stringstream *ds,
1249 std::stringstream *ss)
1250{
92f5a8d4
TL
1251 lock.lock();
1252 auto mod_iter = modules.find(module_command.module_name);
11fdf7f2 1253 if (mod_iter == modules.end()) {
92f5a8d4
TL
1254 *ss << "Module '" << module_command.module_name << "' is not available";
1255 lock.unlock();
11fdf7f2
TL
1256 return -ENOENT;
1257 }
1258
92f5a8d4
TL
1259 lock.unlock();
1260 return mod_iter->second->handle_command(module_command, session, cmdmap,
1261 inbuf, ds, ss);
c07f9fc5
FG
1262}
1263
3efd9988 1264void ActivePyModules::get_health_checks(health_check_map_t *checks)
c07f9fc5 1265{
11fdf7f2 1266 std::lock_guard l(lock);
9f95a23c
TL
1267 for (auto& [name, module] : modules) {
1268 dout(15) << "getting health checks for " << name << dendl;
1269 module->get_health_checks(checks);
c07f9fc5
FG
1270 }
1271}
3efd9988 1272
11fdf7f2
TL
1273void ActivePyModules::update_progress_event(
1274 const std::string& evid,
1275 const std::string& desc,
f67539c2
TL
1276 float progress,
1277 bool add_to_ceph_s)
11fdf7f2
TL
1278{
1279 std::lock_guard l(lock);
1280 auto& pe = progress_events[evid];
1281 pe.message = desc;
1282 pe.progress = progress;
f67539c2 1283 pe.add_to_ceph_s = add_to_ceph_s;
11fdf7f2
TL
1284}
1285
1286void ActivePyModules::complete_progress_event(const std::string& evid)
1287{
1288 std::lock_guard l(lock);
1289 progress_events.erase(evid);
1290}
1291
1292void ActivePyModules::clear_all_progress_events()
1293{
1294 std::lock_guard l(lock);
1295 progress_events.clear();
1296}
1297
1298void ActivePyModules::get_progress_events(std::map<std::string,ProgressEvent> *events)
1299{
1300 std::lock_guard l(lock);
1301 *events = progress_events;
1302}
1303
1304void ActivePyModules::config_notify()
1305{
1306 std::lock_guard l(lock);
9f95a23c 1307 for (auto& [name, module] : modules) {
11fdf7f2
TL
1308 // Send all python calls down a Finisher to avoid blocking
1309 // C++ code, and avoid any potential lock cycles.
9f95a23c
TL
1310 dout(15) << "notify (config) " << name << dendl;
1311 // workaround for https://bugs.llvm.org/show_bug.cgi?id=35984
20effc67 1312 finisher.queue(new LambdaContext([module=module](int r){
9f95a23c
TL
1313 module->config_notify();
1314 }));
11fdf7f2
TL
1315 }
1316}
1317
3efd9988
FG
1318void ActivePyModules::set_uri(const std::string& module_name,
1319 const std::string &uri)
1320{
11fdf7f2 1321 std::lock_guard l(lock);
3efd9988
FG
1322
1323 dout(4) << " module " << module_name << " set URI '" << uri << "'" << dendl;
1324
9f95a23c 1325 modules.at(module_name)->set_uri(uri);
3efd9988
FG
1326}
1327
f67539c2
TL
1328void ActivePyModules::set_device_wear_level(const std::string& devid,
1329 float wear_level)
1330{
1331 // update mgr state
1332 map<string,string> meta;
1333 daemon_state.with_device(
1334 devid,
1335 [wear_level, &meta] (DeviceState& dev) {
1336 dev.set_wear_level(wear_level);
1337 meta = dev.metadata;
1338 });
1339
1340 // tell mon
1341 json_spirit::Object json_object;
1342 for (auto& i : meta) {
1343 json_spirit::Config::add(json_object, i.first, i.second);
1344 }
1345 bufferlist json;
1346 json.append(json_spirit::write(json_object));
1347 const string cmd =
1348 "{"
1349 "\"prefix\": \"config-key set\", "
1350 "\"key\": \"device/" + devid + "\""
1351 "}";
1352
1353 Command set_cmd;
1354 set_cmd.run(&monc, cmd, json);
1355 set_cmd.wait();
1356}
1357
9f95a23c 1358MetricQueryID ActivePyModules::add_osd_perf_query(
11fdf7f2
TL
1359 const OSDPerfMetricQuery &query,
1360 const std::optional<OSDPerfMetricLimit> &limit)
1361{
1362 return server.add_osd_perf_query(query, limit);
1363}
1364
9f95a23c 1365void ActivePyModules::remove_osd_perf_query(MetricQueryID query_id)
11fdf7f2
TL
1366{
1367 int r = server.remove_osd_perf_query(query_id);
1368 if (r < 0) {
1369 dout(0) << "remove_osd_perf_query for query_id=" << query_id << " failed: "
1370 << cpp_strerror(r) << dendl;
1371 }
1372}
1373
9f95a23c 1374PyObject *ActivePyModules::get_osd_perf_counters(MetricQueryID query_id)
11fdf7f2 1375{
f67539c2
TL
1376 OSDPerfCollector collector(query_id);
1377 int r = server.get_osd_perf_counters(&collector);
11fdf7f2
TL
1378 if (r < 0) {
1379 dout(0) << "get_osd_perf_counters for query_id=" << query_id << " failed: "
1380 << cpp_strerror(r) << dendl;
1381 Py_RETURN_NONE;
1382 }
1383
1384 PyFormatter f;
f67539c2
TL
1385 const std::map<OSDPerfMetricKey, PerformanceCounters> &counters = collector.counters;
1386
1387 f.open_array_section("counters");
1388 for (auto &[key, instance_counters] : counters) {
1389 f.open_object_section("i");
1390 f.open_array_section("k");
1391 for (auto &sub_key : key) {
1392 f.open_array_section("s");
1393 for (size_t i = 0; i < sub_key.size(); i++) {
1394 f.dump_string(stringify(i).c_str(), sub_key[i]);
1395 }
1396 f.close_section(); // s
1397 }
1398 f.close_section(); // k
1399 f.open_array_section("c");
1400 for (auto &c : instance_counters) {
1401 f.open_array_section("p");
1402 f.dump_unsigned("0", c.first);
1403 f.dump_unsigned("1", c.second);
1404 f.close_section(); // p
1405 }
1406 f.close_section(); // c
1407 f.close_section(); // i
1408 }
1409 f.close_section(); // counters
1410
1411 return f.get();
1412}
1413
1414MetricQueryID ActivePyModules::add_mds_perf_query(
1415 const MDSPerfMetricQuery &query,
1416 const std::optional<MDSPerfMetricLimit> &limit)
1417{
1418 return server.add_mds_perf_query(query, limit);
1419}
1420
1421void ActivePyModules::remove_mds_perf_query(MetricQueryID query_id)
1422{
1423 int r = server.remove_mds_perf_query(query_id);
1424 if (r < 0) {
1425 dout(0) << "remove_mds_perf_query for query_id=" << query_id << " failed: "
1426 << cpp_strerror(r) << dendl;
1427 }
1428}
1429
33c7a0ef
TL
1430void ActivePyModules::reregister_mds_perf_queries()
1431{
1432 server.reregister_mds_perf_queries();
1433}
1434
f67539c2
TL
1435PyObject *ActivePyModules::get_mds_perf_counters(MetricQueryID query_id)
1436{
1437 MDSPerfCollector collector(query_id);
1438 int r = server.get_mds_perf_counters(&collector);
1439 if (r < 0) {
1440 dout(0) << "get_mds_perf_counters for query_id=" << query_id << " failed: "
1441 << cpp_strerror(r) << dendl;
1442 Py_RETURN_NONE;
1443 }
1444
1445 PyFormatter f;
1446 const std::map<MDSPerfMetricKey, PerformanceCounters> &counters = collector.counters;
1447
1448 f.open_array_section("metrics");
1449
1450 f.open_array_section("delayed_ranks");
1451 f.dump_string("ranks", stringify(collector.delayed_ranks).c_str());
1452 f.close_section(); // delayed_ranks
11fdf7f2
TL
1453
1454 f.open_array_section("counters");
f67539c2 1455 for (auto &[key, instance_counters] : counters) {
11fdf7f2
TL
1456 f.open_object_section("i");
1457 f.open_array_section("k");
1458 for (auto &sub_key : key) {
1459 f.open_array_section("s");
1460 for (size_t i = 0; i < sub_key.size(); i++) {
1461 f.dump_string(stringify(i).c_str(), sub_key[i]);
1462 }
1463 f.close_section(); // s
1464 }
1465 f.close_section(); // k
1466 f.open_array_section("c");
1467 for (auto &c : instance_counters) {
1468 f.open_array_section("p");
1469 f.dump_unsigned("0", c.first);
1470 f.dump_unsigned("1", c.second);
1471 f.close_section(); // p
1472 }
1473 f.close_section(); // c
1474 f.close_section(); // i
1475 }
1476 f.close_section(); // counters
33c7a0ef
TL
1477
1478 f.open_array_section("last_updated");
1479 f.dump_float("last_updated_mono", collector.last_updated_mono);
1480 f.close_section(); // last_updated
1481
f67539c2 1482 f.close_section(); // metrics
11fdf7f2
TL
1483
1484 return f.get();
1485}
1486
1487void ActivePyModules::cluster_log(const std::string &channel, clog_type prio,
1488 const std::string &message)
1489{
1490 std::lock_guard l(lock);
1491
9f95a23c 1492 auto cl = monc.get_log_client()->create_channel(channel);
20effc67 1493 cl->parse_client_options(g_ceph_context);
9f95a23c
TL
1494 cl->do_log(prio, message);
1495}
1496
1497void ActivePyModules::register_client(std::string_view name, std::string addrs)
1498{
1499 std::lock_guard l(lock);
1500
1501 entity_addrvec_t addrv;
1502 addrv.parse(addrs.data());
1503
1504 dout(7) << "registering msgr client handle " << addrv << dendl;
1505 py_module_registry.register_client(name, std::move(addrv));
1506}
1507
1508void ActivePyModules::unregister_client(std::string_view name, std::string addrs)
1509{
1510 std::lock_guard l(lock);
1511
1512 entity_addrvec_t addrv;
1513 addrv.parse(addrs.data());
1514
1515 dout(7) << "unregistering msgr client handle " << addrv << dendl;
1516 py_module_registry.unregister_client(name, addrv);
11fdf7f2 1517}