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