]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/ActivePyModules.cc
update sources to 12.2.7
[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
3efd9988 15#include "BaseMgrModule.h"
31f18b77 16#include "Gil.h"
7c673cae 17
7c673cae
FG
18#include "common/errno.h"
19#include "include/stringify.h"
20
21#include "PyFormatter.h"
22
23#include "osd/OSDMap.h"
24#include "mon/MonMap.h"
25
26#include "mgr/MgrContext.h"
27
3efd9988
FG
28// For ::config_prefix
29#include "PyModuleRegistry.h"
30
31#include "ActivePyModules.h"
7c673cae
FG
32
33#define dout_context g_ceph_context
34#define dout_subsys ceph_subsys_mgr
7c673cae 35#undef dout_prefix
31f18b77 36#define dout_prefix *_dout << "mgr " << __func__ << " "
7c673cae 37
7c673cae 38
3efd9988
FG
39ActivePyModules::ActivePyModules(PyModuleConfig const &config_,
40 DaemonStateIndex &ds, ClusterState &cs,
224ce89b
WB
41 MonClient &mc, LogChannelRef clog_, Objecter &objecter_,
42 Client &client_, Finisher &f)
3efd9988
FG
43 : config_cache(config_), daemon_state(ds), cluster_state(cs),
44 monc(mc), clog(clog_), objecter(objecter_), client(client_), finisher(f),
45 lock("ActivePyModules")
7c673cae
FG
46{}
47
3efd9988 48ActivePyModules::~ActivePyModules() = default;
7c673cae 49
3efd9988 50void ActivePyModules::dump_server(const std::string &hostname,
7c673cae
FG
51 const DaemonStateCollection &dmc,
52 Formatter *f)
53{
54 f->dump_string("hostname", hostname);
55 f->open_array_section("services");
56 std::string ceph_version;
57
58 for (const auto &i : dmc) {
c07f9fc5 59 Mutex::Locker l(i.second->lock);
7c673cae 60 const auto &key = i.first;
224ce89b 61 const std::string &str_type = key.first;
7c673cae
FG
62 const std::string &svc_name = key.second;
63
64 // TODO: pick the highest version, and make sure that
65 // somewhere else (during health reporting?) we are
66 // indicating to the user if we see mixed versions
67 auto ver_iter = i.second->metadata.find("ceph_version");
68 if (ver_iter != i.second->metadata.end()) {
69 ceph_version = i.second->metadata.at("ceph_version");
70 }
71
72 f->open_object_section("service");
73 f->dump_string("type", str_type);
74 f->dump_string("id", svc_name);
75 f->close_section();
76 }
77 f->close_section();
78
79 f->dump_string("ceph_version", ceph_version);
80}
81
82
83
3efd9988 84PyObject *ActivePyModules::get_server_python(const std::string &hostname)
7c673cae
FG
85{
86 PyThreadState *tstate = PyEval_SaveThread();
87 Mutex::Locker l(lock);
88 PyEval_RestoreThread(tstate);
89 dout(10) << " (" << hostname << ")" << dendl;
90
91 auto dmc = daemon_state.get_by_server(hostname);
92
93 PyFormatter f;
94 dump_server(hostname, dmc, &f);
95 return f.get();
96}
97
98
3efd9988 99PyObject *ActivePyModules::list_servers_python()
7c673cae
FG
100{
101 PyThreadState *tstate = PyEval_SaveThread();
102 Mutex::Locker l(lock);
103 PyEval_RestoreThread(tstate);
104 dout(10) << " >" << dendl;
105
106 PyFormatter f(false, true);
3efd9988
FG
107 daemon_state.with_daemons_by_server([this, &f]
108 (const std::map<std::string, DaemonStateCollection> &all) {
109 for (const auto &i : all) {
110 const auto &hostname = i.first;
7c673cae 111
3efd9988
FG
112 f.open_object_section("server");
113 dump_server(hostname, i.second, &f);
114 f.close_section();
115 }
116 });
7c673cae
FG
117
118 return f.get();
119}
120
3efd9988
FG
121PyObject *ActivePyModules::get_metadata_python(
122 const std::string &svc_type,
224ce89b 123 const std::string &svc_id)
7c673cae 124{
3efd9988
FG
125 auto metadata = daemon_state.get(DaemonKey(svc_type, svc_id));
126 if (metadata == nullptr) {
127 derr << "Requested missing service " << svc_type << "." << svc_id << dendl;
128 Py_RETURN_NONE;
129 }
130
c07f9fc5 131 Mutex::Locker l(metadata->lock);
7c673cae
FG
132 PyFormatter f;
133 f.dump_string("hostname", metadata->hostname);
134 for (const auto &i : metadata->metadata) {
135 f.dump_string(i.first.c_str(), i.second);
136 }
137
138 return f.get();
139}
140
3efd9988
FG
141PyObject *ActivePyModules::get_daemon_status_python(
142 const std::string &svc_type,
224ce89b
WB
143 const std::string &svc_id)
144{
3efd9988
FG
145 auto metadata = daemon_state.get(DaemonKey(svc_type, svc_id));
146 if (metadata == nullptr) {
147 derr << "Requested missing service " << svc_type << "." << svc_id << dendl;
148 Py_RETURN_NONE;
149 }
150
c07f9fc5 151 Mutex::Locker l(metadata->lock);
224ce89b
WB
152 PyFormatter f;
153 for (const auto &i : metadata->service_status) {
154 f.dump_string(i.first.c_str(), i.second);
155 }
156 return f.get();
157}
7c673cae 158
3efd9988 159PyObject *ActivePyModules::get_python(const std::string &what)
7c673cae
FG
160{
161 PyThreadState *tstate = PyEval_SaveThread();
162 Mutex::Locker l(lock);
163 PyEval_RestoreThread(tstate);
164
165 if (what == "fs_map") {
166 PyFormatter f;
167 cluster_state.with_fsmap([&f](const FSMap &fsmap) {
168 fsmap.dump(&f);
169 });
170 return f.get();
171 } else if (what == "osdmap_crush_map_text") {
172 bufferlist rdata;
173 cluster_state.with_osdmap([&rdata](const OSDMap &osd_map){
174 osd_map.crush->encode(rdata, CEPH_FEATURES_SUPPORTED_DEFAULT);
175 });
176 std::string crush_text = rdata.to_str();
177 return PyString_FromString(crush_text.c_str());
178 } else if (what.substr(0, 7) == "osd_map") {
179 PyFormatter f;
180 cluster_state.with_osdmap([&f, &what](const OSDMap &osd_map){
181 if (what == "osd_map") {
182 osd_map.dump(&f);
183 } else if (what == "osd_map_tree") {
184 osd_map.print_tree(&f, nullptr);
185 } else if (what == "osd_map_crush") {
186 osd_map.crush->dump(&f);
187 }
188 });
189 return f.get();
190 } else if (what == "config") {
191 PyFormatter f;
192 g_conf->show_config(&f);
193 return f.get();
194 } else if (what == "mon_map") {
195 PyFormatter f;
196 cluster_state.with_monmap(
197 [&f](const MonMap &monmap) {
198 monmap.dump(&f);
199 }
200 );
201 return f.get();
224ce89b
WB
202 } else if (what == "service_map") {
203 PyFormatter f;
204 cluster_state.with_servicemap(
205 [&f](const ServiceMap &service_map) {
206 service_map.dump(&f);
207 }
208 );
209 return f.get();
7c673cae
FG
210 } else if (what == "osd_metadata") {
211 PyFormatter f;
224ce89b 212 auto dmc = daemon_state.get_by_service("osd");
7c673cae 213 for (const auto &i : dmc) {
c07f9fc5 214 Mutex::Locker l(i.second->lock);
7c673cae
FG
215 f.open_object_section(i.first.second.c_str());
216 f.dump_string("hostname", i.second->hostname);
217 for (const auto &j : i.second->metadata) {
218 f.dump_string(j.first.c_str(), j.second);
219 }
220 f.close_section();
221 }
222 return f.get();
223 } else if (what == "pg_summary") {
224 PyFormatter f;
225 cluster_state.with_pgmap(
226 [&f](const PGMap &pg_map) {
227 std::map<std::string, std::map<std::string, uint32_t> > osds;
228 std::map<std::string, std::map<std::string, uint32_t> > pools;
229 std::map<std::string, uint32_t> all;
230 for (const auto &i : pg_map.pg_stat) {
231 const auto pool = i.first.m_pool;
232 const std::string state = pg_state_string(i.second.state);
233 // Insert to per-pool map
234 pools[stringify(pool)][state]++;
235 for (const auto &osd_id : i.second.acting) {
236 osds[stringify(osd_id)][state]++;
237 }
238 all[state]++;
239 }
240 f.open_object_section("by_osd");
241 for (const auto &i : osds) {
242 f.open_object_section(i.first.c_str());
243 for (const auto &j : i.second) {
244 f.dump_int(j.first.c_str(), j.second);
245 }
246 f.close_section();
247 }
248 f.close_section();
249 f.open_object_section("by_pool");
250 for (const auto &i : pools) {
251 f.open_object_section(i.first.c_str());
252 for (const auto &j : i.second) {
253 f.dump_int(j.first.c_str(), j.second);
254 }
255 f.close_section();
256 }
257 f.close_section();
258 f.open_object_section("all");
259 for (const auto &i : all) {
260 f.dump_int(i.first.c_str(), i.second);
261 }
262 f.close_section();
28e407b8
AA
263 f.open_object_section("pg_stats_sum");
264 pg_map.pg_sum.dump(&f);
265 f.close_section();
7c673cae
FG
266 }
267 );
268 return f.get();
3efd9988
FG
269 } else if (what == "pg_status") {
270 PyFormatter f;
271 cluster_state.with_pgmap(
272 [&f](const PGMap &pg_map) {
273 pg_map.print_summary(&f, nullptr);
274 }
275 );
276 return f.get();
277 } else if (what == "pg_dump") {
278 PyFormatter f;
279 cluster_state.with_pgmap(
280 [&f](const PGMap &pg_map) {
281 pg_map.dump(&f);
282 }
283 );
284 return f.get();
7c673cae
FG
285 } else if (what == "df") {
286 PyFormatter f;
287
288 cluster_state.with_osdmap([this, &f](const OSDMap &osd_map){
289 cluster_state.with_pgmap(
290 [&osd_map, &f](const PGMap &pg_map) {
291 pg_map.dump_fs_stats(nullptr, &f, true);
31f18b77 292 pg_map.dump_pool_stats_full(osd_map, nullptr, &f, true);
7c673cae
FG
293 });
294 });
295 return f.get();
296 } else if (what == "osd_stats") {
297 PyFormatter f;
298 cluster_state.with_pgmap(
299 [&f](const PGMap &pg_map) {
300 pg_map.dump_osd_stats(&f);
301 });
302 return f.get();
303 } else if (what == "health" || what == "mon_status") {
304 PyFormatter f;
305 bufferlist json;
306 if (what == "health") {
307 json = cluster_state.get_health();
308 } else if (what == "mon_status") {
309 json = cluster_state.get_mon_status();
310 } else {
311 assert(false);
312 }
313 f.dump_string("json", json.to_str());
314 return f.get();
224ce89b
WB
315 } else if (what == "mgr_map") {
316 PyFormatter f;
317 cluster_state.with_mgrmap([&f](const MgrMap &mgr_map) {
318 mgr_map.dump(&f);
319 });
320 return f.get();
7c673cae
FG
321 } else {
322 derr << "Python module requested unknown data '" << what << "'" << dendl;
323 Py_RETURN_NONE;
324 }
325}
326
3efd9988
FG
327int ActivePyModules::start_one(std::string const &module_name,
328 PyObject *pClass, const SafeThreadState &pMyThreadState)
7c673cae 329{
3efd9988 330 Mutex::Locker l(lock);
7c673cae 331
3efd9988 332 assert(modules.count(module_name) == 0);
7c673cae 333
3efd9988
FG
334 modules[module_name].reset(new ActivePyModule(
335 module_name, pClass,
b32b8144 336 pMyThreadState, clog));
7c673cae 337
3efd9988
FG
338 int r = modules[module_name]->load(this);
339 if (r != 0) {
340 return r;
341 } else {
342 dout(4) << "Starting thread for " << module_name << dendl;
343 // Giving Thread the module's module_name member as its
344 // char* thread name: thread must not outlive module class lifetime.
345 modules[module_name]->thread.create(
346 modules[module_name]->get_name().c_str());
7c673cae 347
3efd9988 348 return 0;
7c673cae
FG
349 }
350}
351
3efd9988 352void ActivePyModules::shutdown()
7c673cae
FG
353{
354 Mutex::Locker locker(lock);
7c673cae
FG
355
356 // Signal modules to drop out of serve() and/or tear down resources
357 for (auto &i : modules) {
358 auto module = i.second.get();
359 const auto& name = i.first;
3efd9988 360
31f18b77 361 lock.Unlock();
3efd9988 362 dout(10) << "calling module " << name << " shutdown()" << dendl;
7c673cae 363 module->shutdown();
3efd9988 364 dout(10) << "module " << name << " shutdown() returned" << dendl;
31f18b77 365 lock.Lock();
7c673cae
FG
366 }
367
368 // For modules implementing serve(), finish the threads where we
369 // were running that.
3efd9988 370 for (auto &i : modules) {
7c673cae 371 lock.Unlock();
3efd9988
FG
372 dout(10) << "joining module " << i.first << dendl;
373 i.second->thread.join();
374 dout(10) << "joined module " << i.first << dendl;
7c673cae
FG
375 lock.Lock();
376 }
7c673cae
FG
377
378 modules.clear();
7c673cae
FG
379}
380
3efd9988 381void ActivePyModules::notify_all(const std::string &notify_type,
7c673cae
FG
382 const std::string &notify_id)
383{
384 Mutex::Locker l(lock);
385
386 dout(10) << __func__ << ": notify_all " << notify_type << dendl;
387 for (auto& i : modules) {
388 auto module = i.second.get();
389 // Send all python calls down a Finisher to avoid blocking
390 // C++ code, and avoid any potential lock cycles.
391 finisher.queue(new FunctionContext([module, notify_type, notify_id](int r){
392 module->notify(notify_type, notify_id);
393 }));
394 }
395}
396
3efd9988 397void ActivePyModules::notify_all(const LogEntry &log_entry)
7c673cae
FG
398{
399 Mutex::Locker l(lock);
400
401 dout(10) << __func__ << ": notify_all (clog)" << dendl;
402 for (auto& i : modules) {
403 auto module = i.second.get();
404 // Send all python calls down a Finisher to avoid blocking
405 // C++ code, and avoid any potential lock cycles.
406 //
407 // Note intentional use of non-reference lambda binding on
408 // log_entry: we take a copy because caller's instance is
409 // probably ephemeral.
410 finisher.queue(new FunctionContext([module, log_entry](int r){
411 module->notify_clog(log_entry);
412 }));
413 }
414}
415
3efd9988 416bool ActivePyModules::get_config(const std::string &module_name,
7c673cae
FG
417 const std::string &key, std::string *val) const
418{
419 PyThreadState *tstate = PyEval_SaveThread();
420 Mutex::Locker l(lock);
421 PyEval_RestoreThread(tstate);
422
3efd9988
FG
423 const std::string global_key = PyModuleRegistry::config_prefix
424 + module_name + "/" + key;
31f18b77
FG
425
426 dout(4) << __func__ << "key: " << global_key << dendl;
7c673cae
FG
427
428 if (config_cache.count(global_key)) {
429 *val = config_cache.at(global_key);
430 return true;
431 } else {
432 return false;
433 }
434}
435
3efd9988 436PyObject *ActivePyModules::get_config_prefix(const std::string &module_name,
31f18b77
FG
437 const std::string &prefix) const
438{
439 PyThreadState *tstate = PyEval_SaveThread();
440 Mutex::Locker l(lock);
441 PyEval_RestoreThread(tstate);
442
3efd9988
FG
443 const std::string base_prefix = PyModuleRegistry::config_prefix
444 + module_name + "/";
31f18b77
FG
445 const std::string global_prefix = base_prefix + prefix;
446 dout(4) << __func__ << "prefix: " << global_prefix << dendl;
447
448 PyFormatter f;
449 for (auto p = config_cache.lower_bound(global_prefix);
450 p != config_cache.end() && p->first.find(global_prefix) == 0;
451 ++p) {
452 f.dump_string(p->first.c_str() + base_prefix.size(), p->second);
453 }
454 return f.get();
455}
456
3efd9988 457void ActivePyModules::set_config(const std::string &module_name,
d2e6a577 458 const std::string &key, const boost::optional<std::string>& val)
7c673cae 459{
3efd9988
FG
460 const std::string global_key = PyModuleRegistry::config_prefix
461 + module_name + "/" + key;
7c673cae
FG
462
463 Command set_cmd;
464 {
465 PyThreadState *tstate = PyEval_SaveThread();
466 Mutex::Locker l(lock);
467 PyEval_RestoreThread(tstate);
d2e6a577
FG
468 if (val) {
469 config_cache[global_key] = *val;
470 } else {
471 config_cache.erase(global_key);
472 }
7c673cae
FG
473
474 std::ostringstream cmd_json;
7c673cae
FG
475 JSONFormatter jf;
476 jf.open_object_section("cmd");
d2e6a577
FG
477 if (val) {
478 jf.dump_string("prefix", "config-key set");
479 jf.dump_string("key", global_key);
480 jf.dump_string("val", *val);
481 } else {
482 jf.dump_string("prefix", "config-key del");
483 jf.dump_string("key", global_key);
484 }
7c673cae
FG
485 jf.close_section();
486 jf.flush(cmd_json);
7c673cae
FG
487 set_cmd.run(&monc, cmd_json.str());
488 }
489 set_cmd.wait();
490
491 if (set_cmd.r != 0) {
c07f9fc5 492 // config-key set will fail if mgr's auth key has insufficient
7c673cae
FG
493 // permission to set config keys
494 // FIXME: should this somehow raise an exception back into Python land?
c07f9fc5 495 dout(0) << "`config-key set " << global_key << " " << val << "` failed: "
7c673cae
FG
496 << cpp_strerror(set_cmd.r) << dendl;
497 dout(0) << "mon returned " << set_cmd.r << ": " << set_cmd.outs << dendl;
498 }
499}
500
3efd9988 501std::vector<ModuleCommand> ActivePyModules::get_py_commands() const
7c673cae
FG
502{
503 Mutex::Locker l(lock);
504
505 std::vector<ModuleCommand> result;
c07f9fc5 506 for (const auto& i : modules) {
7c673cae
FG
507 auto module = i.second.get();
508 auto mod_commands = module->get_commands();
509 for (auto j : mod_commands) {
510 result.push_back(j);
511 }
512 }
513
514 return result;
515}
516
3efd9988 517std::vector<MonCommand> ActivePyModules::get_commands() const
c07f9fc5
FG
518{
519 std::vector<ModuleCommand> commands = get_py_commands();
520 std::vector<MonCommand> result;
521 for (auto &pyc: commands) {
522 result.push_back({pyc.cmdstring, pyc.helpstring, "mgr",
523 pyc.perm, "cli", MonCommand::FLAG_MGR});
524 }
525 return result;
526}
527
3efd9988
FG
528
529std::map<std::string, std::string> ActivePyModules::get_services() const
7c673cae 530{
3efd9988 531 std::map<std::string, std::string> result;
7c673cae 532 Mutex::Locker l(lock);
3efd9988
FG
533 for (const auto& i : modules) {
534 const auto &module = i.second.get();
535 std::string svc_str = module->get_uri();
536 if (!svc_str.empty()) {
537 result[module->get_name()] = svc_str;
538 }
539 }
7c673cae 540
3efd9988 541 return result;
7c673cae
FG
542}
543
3efd9988 544PyObject* ActivePyModules::get_counter_python(
224ce89b 545 const std::string &svc_name,
7c673cae
FG
546 const std::string &svc_id,
547 const std::string &path)
548{
549 PyThreadState *tstate = PyEval_SaveThread();
550 Mutex::Locker l(lock);
551 PyEval_RestoreThread(tstate);
552
553 PyFormatter f;
554 f.open_array_section(path.c_str());
555
224ce89b 556 auto metadata = daemon_state.get(DaemonKey(svc_name, svc_id));
7c673cae 557 if (metadata) {
3efd9988 558 Mutex::Locker l2(metadata->lock);
7c673cae
FG
559 if (metadata->perf_counters.instances.count(path)) {
560 auto counter_instance = metadata->perf_counters.instances.at(path);
28e407b8
AA
561 auto counter_type = metadata->perf_counters.types.at(path);
562 if (counter_type.type & PERFCOUNTER_LONGRUNAVG) {
563 const auto &avg_data = counter_instance.get_data_avg();
564 for (const auto &datapoint : avg_data) {
565 f.open_array_section("datapoint");
566 f.dump_unsigned("t", datapoint.t.sec());
567 f.dump_unsigned("s", datapoint.s);
568 f.dump_unsigned("c", datapoint.c);
569 f.close_section();
570 }
571 } else {
572 const auto &data = counter_instance.get_data();
573 for (const auto &datapoint : data) {
574 f.open_array_section("datapoint");
575 f.dump_unsigned("t", datapoint.t.sec());
576 f.dump_unsigned("v", datapoint.v);
577 f.close_section();
578 }
7c673cae
FG
579 }
580 } else {
581 dout(4) << "Missing counter: '" << path << "' ("
224ce89b 582 << svc_name << "." << svc_id << ")" << dendl;
7c673cae
FG
583 dout(20) << "Paths are:" << dendl;
584 for (const auto &i : metadata->perf_counters.instances) {
585 dout(20) << i.first << dendl;
586 }
587 }
588 } else {
589 dout(4) << "No daemon state for "
224ce89b 590 << svc_name << "." << svc_id << ")" << dendl;
7c673cae
FG
591 }
592 f.close_section();
593 return f.get();
594}
595
3efd9988 596PyObject* ActivePyModules::get_perf_schema_python(
c07f9fc5
FG
597 const std::string svc_type,
598 const std::string &svc_id)
599{
600 PyThreadState *tstate = PyEval_SaveThread();
601 Mutex::Locker l(lock);
602 PyEval_RestoreThread(tstate);
603
3efd9988 604 DaemonStateCollection daemons;
c07f9fc5
FG
605
606 if (svc_type == "") {
3efd9988 607 daemons = std::move(daemon_state.get_all());
c07f9fc5 608 } else if (svc_id.empty()) {
3efd9988 609 daemons = std::move(daemon_state.get_by_service(svc_type));
c07f9fc5
FG
610 } else {
611 auto key = DaemonKey(svc_type, svc_id);
612 // so that the below can be a loop in all cases
3efd9988
FG
613 auto got = daemon_state.get(key);
614 if (got != nullptr) {
615 daemons[key] = got;
c07f9fc5
FG
616 }
617 }
618
619 PyFormatter f;
3efd9988
FG
620 if (!daemons.empty()) {
621 for (auto statepair : daemons) {
c07f9fc5
FG
622 auto key = statepair.first;
623 auto state = statepair.second;
3efd9988
FG
624
625 std::ostringstream daemon_name;
c07f9fc5
FG
626 daemon_name << key.first << "." << key.second;
627 f.open_object_section(daemon_name.str().c_str());
628
3efd9988
FG
629 Mutex::Locker l(state->lock);
630 for (auto ctr_inst_iter : state->perf_counters.instances) {
631 const auto &counter_name = ctr_inst_iter.first;
632 f.open_object_section(counter_name.c_str());
633 auto type = state->perf_counters.types[counter_name];
c07f9fc5
FG
634 f.dump_string("description", type.description);
635 if (!type.nick.empty()) {
636 f.dump_string("nick", type.nick);
637 }
638 f.dump_unsigned("type", type.type);
3efd9988 639 f.dump_unsigned("priority", type.priority);
c07f9fc5
FG
640 f.close_section();
641 }
642 f.close_section();
643 }
644 } else {
645 dout(4) << __func__ << ": No daemon state found for "
646 << svc_type << "." << svc_id << ")" << dendl;
647 }
c07f9fc5
FG
648 return f.get();
649}
650
3efd9988 651PyObject *ActivePyModules::get_context()
7c673cae
FG
652{
653 PyThreadState *tstate = PyEval_SaveThread();
654 Mutex::Locker l(lock);
655 PyEval_RestoreThread(tstate);
656
657 // Construct a capsule containing ceph context.
658 // Not incrementing/decrementing ref count on the context because
659 // it's the global one and it has process lifetime.
660 auto capsule = PyCapsule_New(g_ceph_context, nullptr, nullptr);
661 return capsule;
662}
663
3efd9988
FG
664/**
665 * Helper for our wrapped types that take a capsule in their constructor.
666 */
667PyObject *construct_with_capsule(
668 const std::string &module_name,
669 const std::string &clsname,
670 void *wrapped)
224ce89b 671{
3efd9988
FG
672 // Look up the OSDMap type which we will construct
673 PyObject *module = PyImport_ImportModule(module_name.c_str());
674 if (!module) {
675 derr << "Failed to import python module:" << dendl;
676 derr << handle_pyerror() << dendl;
224ce89b 677 }
3efd9988
FG
678 assert(module);
679
680 PyObject *wrapper_type = PyObject_GetAttrString(
681 module, (const char*)clsname.c_str());
682 if (!wrapper_type) {
683 derr << "Failed to get python type:" << dendl;
684 derr << handle_pyerror() << dendl;
224ce89b 685 }
3efd9988
FG
686 assert(wrapper_type);
687
688 // Construct a capsule containing an OSDMap.
689 auto wrapped_capsule = PyCapsule_New(wrapped, nullptr, nullptr);
690 assert(wrapped_capsule);
691
692 // Construct the python OSDMap
693 auto pArgs = PyTuple_Pack(1, wrapped_capsule);
694 auto wrapper_instance = PyObject_CallObject(wrapper_type, pArgs);
695 if (wrapper_instance == nullptr) {
696 derr << "Failed to construct python OSDMap:" << dendl;
697 derr << handle_pyerror() << dendl;
698 }
699 assert(wrapper_instance != nullptr);
700 Py_DECREF(pArgs);
701 Py_DECREF(wrapped_capsule);
702
703 Py_DECREF(wrapper_type);
704 Py_DECREF(module);
705
706 return wrapper_instance;
224ce89b
WB
707}
708
3efd9988 709PyObject *ActivePyModules::get_osdmap()
224ce89b 710{
3efd9988
FG
711 PyThreadState *tstate = PyEval_SaveThread();
712 Mutex::Locker l(lock);
713 PyEval_RestoreThread(tstate);
714
715 OSDMap *newmap = new OSDMap;
716
717 cluster_state.with_osdmap([&](const OSDMap& o) {
718 newmap->deepish_copy_from(o);
719 });
720
721 return construct_with_capsule("mgr_module", "OSDMap", (void*)newmap);
224ce89b 722}
c07f9fc5 723
3efd9988 724void ActivePyModules::set_health_checks(const std::string& module_name,
c07f9fc5
FG
725 health_check_map_t&& checks)
726{
727 Mutex::Locker l(lock);
3efd9988 728 auto p = modules.find(module_name);
c07f9fc5
FG
729 if (p != modules.end()) {
730 p->second->set_health_checks(std::move(checks));
731 }
732}
733
3efd9988 734void ActivePyModules::get_health_checks(health_check_map_t *checks)
c07f9fc5
FG
735{
736 Mutex::Locker l(lock);
737 for (auto& p : modules) {
738 p.second->get_health_checks(checks);
739 }
740}
3efd9988
FG
741
742void ActivePyModules::set_uri(const std::string& module_name,
743 const std::string &uri)
744{
745 Mutex::Locker l(lock);
746
747 dout(4) << " module " << module_name << " set URI '" << uri << "'" << dendl;
748
749 modules[module_name]->set_uri(uri);
750}
751