1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2014 John Spray <john.spray@inktank.com>
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.
14 // Include this first to get python headers earlier
17 #include "common/errno.h"
18 #include "include/stringify.h"
20 #include "PyFormatter.h"
22 #include "osd/OSDMap.h"
23 #include "mon/MonMap.h"
25 #include "mgr/MgrContext.h"
27 // For ::config_prefix
29 #include "PyModuleRegistry.h"
31 #include "ActivePyModules.h"
32 #include "DaemonServer.h"
34 #define dout_context g_ceph_context
35 #define dout_subsys ceph_subsys_mgr
37 #define dout_prefix *_dout << "mgr " << __func__ << " "
39 ActivePyModules::ActivePyModules(PyModuleConfig
&module_config_
,
40 std::map
<std::string
, std::string
> store_data
,
41 DaemonStateIndex
&ds
, ClusterState
&cs
,
42 MonClient
&mc
, LogChannelRef clog_
,
43 LogChannelRef audit_clog_
, Objecter
&objecter_
,
44 Client
&client_
, Finisher
&f
, DaemonServer
&server
,
45 PyModuleRegistry
&pmr
)
46 : module_config(module_config_
), daemon_state(ds
), cluster_state(cs
),
47 monc(mc
), clog(clog_
), audit_clog(audit_clog_
), objecter(objecter_
),
48 client(client_
), finisher(f
),
49 cmd_finisher(g_ceph_context
, "cmd_finisher", "cmdfin"),
50 server(server
), py_module_registry(pmr
), lock("ActivePyModules")
52 store_cache
= std::move(store_data
);
56 ActivePyModules::~ActivePyModules() = default;
58 void ActivePyModules::dump_server(const std::string
&hostname
,
59 const DaemonStateCollection
&dmc
,
62 f
->dump_string("hostname", hostname
);
63 f
->open_array_section("services");
64 std::string ceph_version
;
66 for (const auto &i
: dmc
) {
67 std::lock_guard
l(i
.second
->lock
);
68 const auto &key
= i
.first
;
69 const std::string
&str_type
= key
.first
;
70 const std::string
&svc_name
= key
.second
;
72 // TODO: pick the highest version, and make sure that
73 // somewhere else (during health reporting?) we are
74 // indicating to the user if we see mixed versions
75 auto ver_iter
= i
.second
->metadata
.find("ceph_version");
76 if (ver_iter
!= i
.second
->metadata
.end()) {
77 ceph_version
= i
.second
->metadata
.at("ceph_version");
80 f
->open_object_section("service");
81 f
->dump_string("type", str_type
);
82 f
->dump_string("id", svc_name
);
87 f
->dump_string("ceph_version", ceph_version
);
92 PyObject
*ActivePyModules::get_server_python(const std::string
&hostname
)
94 PyThreadState
*tstate
= PyEval_SaveThread();
95 std::lock_guard
l(lock
);
96 PyEval_RestoreThread(tstate
);
97 dout(10) << " (" << hostname
<< ")" << dendl
;
99 auto dmc
= daemon_state
.get_by_server(hostname
);
102 dump_server(hostname
, dmc
, &f
);
107 PyObject
*ActivePyModules::list_servers_python()
109 PyFormatter
f(false, true);
110 PyThreadState
*tstate
= PyEval_SaveThread();
111 dout(10) << " >" << dendl
;
113 daemon_state
.with_daemons_by_server([this, &f
, &tstate
]
114 (const std::map
<std::string
, DaemonStateCollection
> &all
) {
115 PyEval_RestoreThread(tstate
);
117 for (const auto &i
: all
) {
118 const auto &hostname
= i
.first
;
120 f
.open_object_section("server");
121 dump_server(hostname
, i
.second
, &f
);
129 PyObject
*ActivePyModules::get_metadata_python(
130 const std::string
&svc_type
,
131 const std::string
&svc_id
)
133 auto metadata
= daemon_state
.get(DaemonKey(svc_type
, svc_id
));
134 if (metadata
== nullptr) {
135 derr
<< "Requested missing service " << svc_type
<< "." << svc_id
<< dendl
;
139 std::lock_guard
l(metadata
->lock
);
141 f
.dump_string("hostname", metadata
->hostname
);
142 for (const auto &i
: metadata
->metadata
) {
143 f
.dump_string(i
.first
.c_str(), i
.second
);
149 PyObject
*ActivePyModules::get_daemon_status_python(
150 const std::string
&svc_type
,
151 const std::string
&svc_id
)
153 auto metadata
= daemon_state
.get(DaemonKey(svc_type
, svc_id
));
154 if (metadata
== nullptr) {
155 derr
<< "Requested missing service " << svc_type
<< "." << svc_id
<< dendl
;
159 std::lock_guard
l(metadata
->lock
);
161 for (const auto &i
: metadata
->service_status
) {
162 f
.dump_string(i
.first
.c_str(), i
.second
);
167 PyObject
*ActivePyModules::get_python(const std::string
&what
)
171 // Drop the GIL, as most of the following blocks will block on
172 // a mutex -- they are all responsible for re-taking the GIL before
173 // touching the PyFormatter instance or returning from the function.
174 PyThreadState
*tstate
= PyEval_SaveThread();
176 if (what
== "fs_map") {
177 cluster_state
.with_fsmap([&f
, &tstate
](const FSMap
&fsmap
) {
178 PyEval_RestoreThread(tstate
);
182 } else if (what
== "osdmap_crush_map_text") {
184 cluster_state
.with_osdmap([&rdata
, &tstate
](const OSDMap
&osd_map
){
185 PyEval_RestoreThread(tstate
);
186 osd_map
.crush
->encode(rdata
, CEPH_FEATURES_SUPPORTED_DEFAULT
);
188 std::string crush_text
= rdata
.to_str();
189 return PyString_FromString(crush_text
.c_str());
190 } else if (what
.substr(0, 7) == "osd_map") {
191 cluster_state
.with_osdmap([&f
, &what
, &tstate
](const OSDMap
&osd_map
){
192 PyEval_RestoreThread(tstate
);
193 if (what
== "osd_map") {
195 } else if (what
== "osd_map_tree") {
196 osd_map
.print_tree(&f
, nullptr);
197 } else if (what
== "osd_map_crush") {
198 osd_map
.crush
->dump(&f
);
202 } else if (what
== "modified_config_options") {
203 PyEval_RestoreThread(tstate
);
204 auto all_daemons
= daemon_state
.get_all();
206 for (auto& [key
, daemon
] : all_daemons
) {
207 std::lock_guard
l(daemon
->lock
);
208 for (auto& [name
, valmap
] : daemon
->config
) {
212 f
.open_array_section("options");
213 for (auto& name
: names
) {
214 f
.dump_string("name", name
);
218 } else if (what
.substr(0, 6) == "config") {
219 PyEval_RestoreThread(tstate
);
220 if (what
== "config_options") {
221 g_conf().config_options(&f
);
222 } else if (what
== "config") {
223 g_conf().show_config(&f
);
226 } else if (what
== "mon_map") {
227 cluster_state
.with_monmap(
228 [&f
, &tstate
](const MonMap
&monmap
) {
229 PyEval_RestoreThread(tstate
);
234 } else if (what
== "service_map") {
235 cluster_state
.with_servicemap(
236 [&f
, &tstate
](const ServiceMap
&service_map
) {
237 PyEval_RestoreThread(tstate
);
238 service_map
.dump(&f
);
242 } else if (what
== "osd_metadata") {
243 auto dmc
= daemon_state
.get_by_service("osd");
244 PyEval_RestoreThread(tstate
);
246 for (const auto &i
: dmc
) {
247 std::lock_guard
l(i
.second
->lock
);
248 f
.open_object_section(i
.first
.second
.c_str());
249 f
.dump_string("hostname", i
.second
->hostname
);
250 for (const auto &j
: i
.second
->metadata
) {
251 f
.dump_string(j
.first
.c_str(), j
.second
);
256 } else if (what
== "pg_summary") {
257 cluster_state
.with_pgmap(
258 [&f
, &tstate
](const PGMap
&pg_map
) {
259 PyEval_RestoreThread(tstate
);
261 std::map
<std::string
, std::map
<std::string
, uint32_t> > osds
;
262 std::map
<std::string
, std::map
<std::string
, uint32_t> > pools
;
263 std::map
<std::string
, uint32_t> all
;
264 for (const auto &i
: pg_map
.pg_stat
) {
265 const auto pool
= i
.first
.m_pool
;
266 const std::string state
= pg_state_string(i
.second
.state
);
267 // Insert to per-pool map
268 pools
[stringify(pool
)][state
]++;
269 for (const auto &osd_id
: i
.second
.acting
) {
270 osds
[stringify(osd_id
)][state
]++;
274 f
.open_object_section("by_osd");
275 for (const auto &i
: osds
) {
276 f
.open_object_section(i
.first
.c_str());
277 for (const auto &j
: i
.second
) {
278 f
.dump_int(j
.first
.c_str(), j
.second
);
283 f
.open_object_section("by_pool");
284 for (const auto &i
: pools
) {
285 f
.open_object_section(i
.first
.c_str());
286 for (const auto &j
: i
.second
) {
287 f
.dump_int(j
.first
.c_str(), j
.second
);
292 f
.open_object_section("all");
293 for (const auto &i
: all
) {
294 f
.dump_int(i
.first
.c_str(), i
.second
);
297 f
.open_object_section("pg_stats_sum");
298 pg_map
.pg_sum
.dump(&f
);
303 } else if (what
== "pg_status") {
304 cluster_state
.with_pgmap(
305 [&f
, &tstate
](const PGMap
&pg_map
) {
306 PyEval_RestoreThread(tstate
);
307 pg_map
.print_summary(&f
, nullptr);
311 } else if (what
== "pg_dump") {
312 cluster_state
.with_pgmap(
313 [&f
, &tstate
](const PGMap
&pg_map
) {
314 PyEval_RestoreThread(tstate
);
319 } else if (what
== "devices") {
320 daemon_state
.with_devices2(
322 PyEval_RestoreThread(tstate
);
323 f
.open_array_section("devices");
325 [&f
] (const DeviceState
& dev
) {
326 f
.dump_object("device", dev
);
330 } else if (what
.size() > 7 &&
331 what
.substr(0, 7) == "device ") {
332 string devid
= what
.substr(7);
333 if (!daemon_state
.with_device(
335 [&f
, &tstate
] (const DeviceState
& dev
) {
336 PyEval_RestoreThread(tstate
);
337 f
.dump_object("device", dev
);
340 PyEval_RestoreThread(tstate
);
343 } else if (what
== "io_rate") {
344 cluster_state
.with_pgmap(
345 [&f
, &tstate
](const PGMap
&pg_map
) {
346 PyEval_RestoreThread(tstate
);
347 pg_map
.dump_delta(&f
);
351 } else if (what
== "df") {
352 cluster_state
.with_osdmap_and_pgmap(
354 const OSDMap
& osd_map
,
355 const PGMap
&pg_map
) {
356 PyEval_RestoreThread(tstate
);
357 pg_map
.dump_cluster_stats(nullptr, &f
, true);
358 pg_map
.dump_pool_stats_full(osd_map
, nullptr, &f
, true);
361 } else if (what
== "osd_stats") {
362 cluster_state
.with_pgmap(
363 [&f
, &tstate
](const PGMap
&pg_map
) {
364 PyEval_RestoreThread(tstate
);
365 pg_map
.dump_osd_stats(&f
);
368 } else if (what
== "osd_pool_stats") {
369 int64_t poolid
= -ENOENT
;
371 cluster_state
.with_osdmap_and_pgmap([&](const OSDMap
& osdmap
,
372 const PGMap
& pg_map
) {
373 PyEval_RestoreThread(tstate
);
374 f
.open_array_section("pool_stats");
375 for (auto &p
: osdmap
.get_pools()) {
377 pg_map
.dump_pool_stats_and_io_rate(poolid
, osdmap
, &f
, nullptr);
382 } else if (what
== "health" || what
== "mon_status") {
384 if (what
== "health") {
385 json
= cluster_state
.get_health();
386 } else if (what
== "mon_status") {
387 json
= cluster_state
.get_mon_status();
392 PyEval_RestoreThread(tstate
);
393 f
.dump_string("json", json
.to_str());
395 } else if (what
== "mgr_map") {
396 cluster_state
.with_mgrmap([&f
, &tstate
](const MgrMap
&mgr_map
) {
397 PyEval_RestoreThread(tstate
);
402 derr
<< "Python module requested unknown data '" << what
<< "'" << dendl
;
403 PyEval_RestoreThread(tstate
);
408 void ActivePyModules::start_one(PyModuleRef py_module
)
410 std::lock_guard
l(lock
);
412 ceph_assert(modules
.count(py_module
->get_name()) == 0);
414 const auto name
= py_module
->get_name();
415 modules
[name
].reset(new ActivePyModule(py_module
, clog
));
416 auto active_module
= modules
.at(name
).get();
418 // Send all python calls down a Finisher to avoid blocking
419 // C++ code, and avoid any potential lock cycles.
420 finisher
.queue(new FunctionContext([this, active_module
, name
](int) {
421 int r
= active_module
->load(this);
423 derr
<< "Failed to run module in active mode ('" << name
<< "')"
425 std::lock_guard
l(lock
);
428 dout(4) << "Starting thread for " << name
<< dendl
;
429 active_module
->thread
.create(active_module
->get_thread_name());
434 void ActivePyModules::shutdown()
436 std::lock_guard
locker(lock
);
438 // Signal modules to drop out of serve() and/or tear down resources
439 for (auto &i
: modules
) {
440 auto module
= i
.second
.get();
441 const auto& name
= i
.first
;
444 dout(10) << "calling module " << name
<< " shutdown()" << dendl
;
446 dout(10) << "module " << name
<< " shutdown() returned" << dendl
;
450 // For modules implementing serve(), finish the threads where we
451 // were running that.
452 for (auto &i
: modules
) {
454 dout(10) << "joining module " << i
.first
<< dendl
;
455 i
.second
->thread
.join();
456 dout(10) << "joined module " << i
.first
<< dendl
;
460 cmd_finisher
.wait_for_empty();
466 void ActivePyModules::notify_all(const std::string
¬ify_type
,
467 const std::string
¬ify_id
)
469 std::lock_guard
l(lock
);
471 dout(10) << __func__
<< ": notify_all " << notify_type
<< dendl
;
472 for (auto& i
: modules
) {
473 auto module
= i
.second
.get();
474 // Send all python calls down a Finisher to avoid blocking
475 // C++ code, and avoid any potential lock cycles.
476 finisher
.queue(new FunctionContext([module
, notify_type
, notify_id
](int r
){
477 module
->notify(notify_type
, notify_id
);
482 void ActivePyModules::notify_all(const LogEntry
&log_entry
)
484 std::lock_guard
l(lock
);
486 dout(10) << __func__
<< ": notify_all (clog)" << dendl
;
487 for (auto& i
: modules
) {
488 auto module
= i
.second
.get();
489 // Send all python calls down a Finisher to avoid blocking
490 // C++ code, and avoid any potential lock cycles.
492 // Note intentional use of non-reference lambda binding on
493 // log_entry: we take a copy because caller's instance is
494 // probably ephemeral.
495 finisher
.queue(new FunctionContext([module
, log_entry
](int r
){
496 module
->notify_clog(log_entry
);
501 bool ActivePyModules::get_store(const std::string
&module_name
,
502 const std::string
&key
, std::string
*val
) const
504 PyThreadState
*tstate
= PyEval_SaveThread();
505 std::lock_guard
l(lock
);
506 PyEval_RestoreThread(tstate
);
508 const std::string global_key
= PyModule::config_prefix
509 + module_name
+ "/" + key
;
511 dout(4) << __func__
<< " key: " << global_key
<< dendl
;
513 auto i
= store_cache
.find(global_key
);
514 if (i
!= store_cache
.end()) {
522 PyObject
*ActivePyModules::dispatch_remote(
523 const std::string
&other_module
,
524 const std::string
&method
,
529 auto mod_iter
= modules
.find(other_module
);
530 ceph_assert(mod_iter
!= modules
.end());
532 return mod_iter
->second
->dispatch_remote(method
, args
, kwargs
, err
);
535 bool ActivePyModules::get_config(const std::string
&module_name
,
536 const std::string
&key
, std::string
*val
) const
538 const std::string global_key
= PyModule::config_prefix
539 + module_name
+ "/" + key
;
541 dout(4) << __func__
<< " key: " << global_key
<< dendl
;
543 std::lock_guard
lock(module_config
.lock
);
545 auto i
= module_config
.config
.find(global_key
);
546 if (i
!= module_config
.config
.end()) {
554 PyObject
*ActivePyModules::get_typed_config(
555 const std::string
&module_name
,
556 const std::string
&key
,
557 const std::string
&prefix
) const
559 PyThreadState
*tstate
= PyEval_SaveThread();
561 std::string final_key
;
564 final_key
= prefix
+ "/" + key
;
565 found
= get_config(module_name
, final_key
, &value
);
569 found
= get_config(module_name
, final_key
, &value
);
572 PyModuleRef module
= py_module_registry
.get_module(module_name
);
573 PyEval_RestoreThread(tstate
);
575 derr
<< "Module '" << module_name
<< "' is not available" << dendl
;
578 dout(10) << __func__
<< " " << final_key
<< " found: " << value
<< dendl
;
579 return module
->get_typed_option_value(key
, value
);
581 PyEval_RestoreThread(tstate
);
583 dout(4) << __func__
<< " [" << prefix
<< "/]" << key
<< " not found "
586 dout(4) << __func__
<< " " << key
<< " not found " << dendl
;
591 PyObject
*ActivePyModules::get_store_prefix(const std::string
&module_name
,
592 const std::string
&prefix
) const
594 PyThreadState
*tstate
= PyEval_SaveThread();
595 std::lock_guard
l(lock
);
596 std::lock_guard
lock(module_config
.lock
);
597 PyEval_RestoreThread(tstate
);
599 const std::string base_prefix
= PyModule::config_prefix
601 const std::string global_prefix
= base_prefix
+ prefix
;
602 dout(4) << __func__
<< " prefix: " << global_prefix
<< dendl
;
606 for (auto p
= store_cache
.lower_bound(global_prefix
);
607 p
!= store_cache
.end() && p
->first
.find(global_prefix
) == 0;
609 f
.dump_string(p
->first
.c_str() + base_prefix
.size(), p
->second
);
614 void ActivePyModules::set_store(const std::string
&module_name
,
615 const std::string
&key
, const boost::optional
<std::string
>& val
)
617 const std::string global_key
= PyModule::config_prefix
618 + module_name
+ "/" + key
;
622 std::lock_guard
l(lock
);
624 store_cache
[global_key
] = *val
;
626 store_cache
.erase(global_key
);
629 std::ostringstream cmd_json
;
631 jf
.open_object_section("cmd");
633 jf
.dump_string("prefix", "config-key set");
634 jf
.dump_string("key", global_key
);
635 jf
.dump_string("val", *val
);
637 jf
.dump_string("prefix", "config-key del");
638 jf
.dump_string("key", global_key
);
642 set_cmd
.run(&monc
, cmd_json
.str());
646 if (set_cmd
.r
!= 0) {
647 // config-key set will fail if mgr's auth key has insufficient
648 // permission to set config keys
649 // FIXME: should this somehow raise an exception back into Python land?
650 dout(0) << "`config-key set " << global_key
<< " " << val
<< "` failed: "
651 << cpp_strerror(set_cmd
.r
) << dendl
;
652 dout(0) << "mon returned " << set_cmd
.r
<< ": " << set_cmd
.outs
<< dendl
;
656 void ActivePyModules::set_config(const std::string
&module_name
,
657 const std::string
&key
, const boost::optional
<std::string
>& val
)
659 module_config
.set_config(&monc
, module_name
, key
, val
);
662 std::map
<std::string
, std::string
> ActivePyModules::get_services() const
664 std::map
<std::string
, std::string
> result
;
665 std::lock_guard
l(lock
);
666 for (const auto& i
: modules
) {
667 const auto &module
= i
.second
.get();
668 std::string svc_str
= module
->get_uri();
669 if (!svc_str
.empty()) {
670 result
[module
->get_name()] = svc_str
;
677 PyObject
* ActivePyModules::with_perf_counters(
678 std::function
<void(PerfCounterInstance
& counter_instance
, PerfCounterType
& counter_type
, PyFormatter
& f
)> fct
,
679 const std::string
&svc_name
,
680 const std::string
&svc_id
,
681 const std::string
&path
) const
683 PyThreadState
*tstate
= PyEval_SaveThread();
684 std::lock_guard
l(lock
);
685 PyEval_RestoreThread(tstate
);
688 f
.open_array_section(path
.c_str());
690 auto metadata
= daemon_state
.get(DaemonKey(svc_name
, svc_id
));
692 std::lock_guard
l2(metadata
->lock
);
693 if (metadata
->perf_counters
.instances
.count(path
)) {
694 auto counter_instance
= metadata
->perf_counters
.instances
.at(path
);
695 auto counter_type
= metadata
->perf_counters
.types
.at(path
);
696 fct(counter_instance
, counter_type
, f
);
698 dout(4) << "Missing counter: '" << path
<< "' ("
699 << svc_name
<< "." << svc_id
<< ")" << dendl
;
700 dout(20) << "Paths are:" << dendl
;
701 for (const auto &i
: metadata
->perf_counters
.instances
) {
702 dout(20) << i
.first
<< dendl
;
706 dout(4) << "No daemon state for "
707 << svc_name
<< "." << svc_id
<< ")" << dendl
;
713 PyObject
* ActivePyModules::get_counter_python(
714 const std::string
&svc_name
,
715 const std::string
&svc_id
,
716 const std::string
&path
)
718 auto extract_counters
= [](
719 PerfCounterInstance
& counter_instance
,
720 PerfCounterType
& counter_type
,
723 if (counter_type
.type
& PERFCOUNTER_LONGRUNAVG
) {
724 const auto &avg_data
= counter_instance
.get_data_avg();
725 for (const auto &datapoint
: avg_data
) {
726 f
.open_array_section("datapoint");
727 f
.dump_unsigned("t", datapoint
.t
.sec());
728 f
.dump_unsigned("s", datapoint
.s
);
729 f
.dump_unsigned("c", datapoint
.c
);
733 const auto &data
= counter_instance
.get_data();
734 for (const auto &datapoint
: data
) {
735 f
.open_array_section("datapoint");
736 f
.dump_unsigned("t", datapoint
.t
.sec());
737 f
.dump_unsigned("v", datapoint
.v
);
742 return with_perf_counters(extract_counters
, svc_name
, svc_id
, path
);
745 PyObject
* ActivePyModules::get_latest_counter_python(
746 const std::string
&svc_name
,
747 const std::string
&svc_id
,
748 const std::string
&path
)
750 auto extract_latest_counters
= [](
751 PerfCounterInstance
& counter_instance
,
752 PerfCounterType
& counter_type
,
755 if (counter_type
.type
& PERFCOUNTER_LONGRUNAVG
) {
756 const auto &datapoint
= counter_instance
.get_latest_data_avg();
757 f
.dump_unsigned("t", datapoint
.t
.sec());
758 f
.dump_unsigned("s", datapoint
.s
);
759 f
.dump_unsigned("c", datapoint
.c
);
761 const auto &datapoint
= counter_instance
.get_latest_data();
762 f
.dump_unsigned("t", datapoint
.t
.sec());
763 f
.dump_unsigned("v", datapoint
.v
);
766 return with_perf_counters(extract_latest_counters
, svc_name
, svc_id
, path
);
769 PyObject
* ActivePyModules::get_perf_schema_python(
770 const std::string
&svc_type
,
771 const std::string
&svc_id
)
773 PyThreadState
*tstate
= PyEval_SaveThread();
774 std::lock_guard
l(lock
);
775 PyEval_RestoreThread(tstate
);
777 DaemonStateCollection daemons
;
779 if (svc_type
== "") {
780 daemons
= daemon_state
.get_all();
781 } else if (svc_id
.empty()) {
782 daemons
= daemon_state
.get_by_service(svc_type
);
784 auto key
= DaemonKey(svc_type
, svc_id
);
785 // so that the below can be a loop in all cases
786 auto got
= daemon_state
.get(key
);
787 if (got
!= nullptr) {
793 if (!daemons
.empty()) {
794 for (auto statepair
: daemons
) {
795 auto key
= statepair
.first
;
796 auto state
= statepair
.second
;
798 std::ostringstream daemon_name
;
799 daemon_name
<< key
.first
<< "." << key
.second
;
800 f
.open_object_section(daemon_name
.str().c_str());
802 std::lock_guard
l(state
->lock
);
803 for (auto ctr_inst_iter
: state
->perf_counters
.instances
) {
804 const auto &counter_name
= ctr_inst_iter
.first
;
805 f
.open_object_section(counter_name
.c_str());
806 auto type
= state
->perf_counters
.types
[counter_name
];
807 f
.dump_string("description", type
.description
);
808 if (!type
.nick
.empty()) {
809 f
.dump_string("nick", type
.nick
);
811 f
.dump_unsigned("type", type
.type
);
812 f
.dump_unsigned("priority", type
.priority
);
813 f
.dump_unsigned("units", type
.unit
);
819 dout(4) << __func__
<< ": No daemon state found for "
820 << svc_type
<< "." << svc_id
<< ")" << dendl
;
825 PyObject
*ActivePyModules::get_context()
827 PyThreadState
*tstate
= PyEval_SaveThread();
828 std::lock_guard
l(lock
);
829 PyEval_RestoreThread(tstate
);
831 // Construct a capsule containing ceph context.
832 // Not incrementing/decrementing ref count on the context because
833 // it's the global one and it has process lifetime.
834 auto capsule
= PyCapsule_New(g_ceph_context
, nullptr, nullptr);
839 * Helper for our wrapped types that take a capsule in their constructor.
841 PyObject
*construct_with_capsule(
842 const std::string
&module_name
,
843 const std::string
&clsname
,
846 // Look up the OSDMap type which we will construct
847 PyObject
*module
= PyImport_ImportModule(module_name
.c_str());
849 derr
<< "Failed to import python module:" << dendl
;
850 derr
<< handle_pyerror() << dendl
;
854 PyObject
*wrapper_type
= PyObject_GetAttrString(
855 module
, (const char*)clsname
.c_str());
857 derr
<< "Failed to get python type:" << dendl
;
858 derr
<< handle_pyerror() << dendl
;
860 ceph_assert(wrapper_type
);
862 // Construct a capsule containing an OSDMap.
863 auto wrapped_capsule
= PyCapsule_New(wrapped
, nullptr, nullptr);
864 ceph_assert(wrapped_capsule
);
866 // Construct the python OSDMap
867 auto pArgs
= PyTuple_Pack(1, wrapped_capsule
);
868 auto wrapper_instance
= PyObject_CallObject(wrapper_type
, pArgs
);
869 if (wrapper_instance
== nullptr) {
870 derr
<< "Failed to construct python OSDMap:" << dendl
;
871 derr
<< handle_pyerror() << dendl
;
873 ceph_assert(wrapper_instance
!= nullptr);
875 Py_DECREF(wrapped_capsule
);
877 Py_DECREF(wrapper_type
);
880 return wrapper_instance
;
883 PyObject
*ActivePyModules::get_osdmap()
885 OSDMap
*newmap
= new OSDMap
;
887 PyThreadState
*tstate
= PyEval_SaveThread();
889 std::lock_guard
l(lock
);
890 cluster_state
.with_osdmap([&](const OSDMap
& o
) {
891 newmap
->deepish_copy_from(o
);
894 PyEval_RestoreThread(tstate
);
896 return construct_with_capsule("mgr_module", "OSDMap", (void*)newmap
);
899 void ActivePyModules::set_health_checks(const std::string
& module_name
,
900 health_check_map_t
&& checks
)
902 bool changed
= false;
905 auto p
= modules
.find(module_name
);
906 if (p
!= modules
.end()) {
907 changed
= p
->second
->set_health_checks(std::move(checks
));
911 // immediately schedule a report to be sent to the monitors with the new
912 // health checks that have changed. This is done asynchronusly to avoid
913 // blocking python land. ActivePyModules::lock needs to be dropped to make
916 // send_report callers: DaemonServer::lock -> PyModuleRegistery::lock
917 // active_start: PyModuleRegistry::lock -> ActivePyModules::lock
919 // if we don't release this->lock before calling schedule_tick a cycle is
920 // formed with the addition of ActivePyModules::lock -> DaemonServer::lock.
921 // This is still correct as send_report is run asynchronously under
922 // DaemonServer::lock.
924 server
.schedule_tick(0);
927 int ActivePyModules::handle_command(
928 std::string
const &module_name
,
929 const cmdmap_t
&cmdmap
,
930 const bufferlist
&inbuf
,
931 std::stringstream
*ds
,
932 std::stringstream
*ss
)
935 auto mod_iter
= modules
.find(module_name
);
936 if (mod_iter
== modules
.end()) {
937 *ss
<< "Module '" << module_name
<< "' is not available";
943 return mod_iter
->second
->handle_command(cmdmap
, inbuf
, ds
, ss
);
946 void ActivePyModules::get_health_checks(health_check_map_t
*checks
)
948 std::lock_guard
l(lock
);
949 for (auto& p
: modules
) {
950 p
.second
->get_health_checks(checks
);
954 void ActivePyModules::update_progress_event(
955 const std::string
& evid
,
956 const std::string
& desc
,
959 std::lock_guard
l(lock
);
960 auto& pe
= progress_events
[evid
];
962 pe
.progress
= progress
;
965 void ActivePyModules::complete_progress_event(const std::string
& evid
)
967 std::lock_guard
l(lock
);
968 progress_events
.erase(evid
);
971 void ActivePyModules::clear_all_progress_events()
973 std::lock_guard
l(lock
);
974 progress_events
.clear();
977 void ActivePyModules::get_progress_events(std::map
<std::string
,ProgressEvent
> *events
)
979 std::lock_guard
l(lock
);
980 *events
= progress_events
;
983 void ActivePyModules::config_notify()
985 std::lock_guard
l(lock
);
986 for (auto& i
: modules
) {
987 auto module
= i
.second
.get();
988 // Send all python calls down a Finisher to avoid blocking
989 // C++ code, and avoid any potential lock cycles.
990 finisher
.queue(new FunctionContext([module
](int r
){
991 module
->config_notify();
996 void ActivePyModules::set_uri(const std::string
& module_name
,
997 const std::string
&uri
)
999 std::lock_guard
l(lock
);
1001 dout(4) << " module " << module_name
<< " set URI '" << uri
<< "'" << dendl
;
1003 modules
[module_name
]->set_uri(uri
);
1006 OSDPerfMetricQueryID
ActivePyModules::add_osd_perf_query(
1007 const OSDPerfMetricQuery
&query
,
1008 const std::optional
<OSDPerfMetricLimit
> &limit
)
1010 return server
.add_osd_perf_query(query
, limit
);
1013 void ActivePyModules::remove_osd_perf_query(OSDPerfMetricQueryID query_id
)
1015 int r
= server
.remove_osd_perf_query(query_id
);
1017 dout(0) << "remove_osd_perf_query for query_id=" << query_id
<< " failed: "
1018 << cpp_strerror(r
) << dendl
;
1022 PyObject
*ActivePyModules::get_osd_perf_counters(OSDPerfMetricQueryID query_id
)
1024 std::map
<OSDPerfMetricKey
, PerformanceCounters
> counters
;
1026 int r
= server
.get_osd_perf_counters(query_id
, &counters
);
1028 dout(0) << "get_osd_perf_counters for query_id=" << query_id
<< " failed: "
1029 << cpp_strerror(r
) << dendl
;
1035 f
.open_array_section("counters");
1036 for (auto &it
: counters
) {
1037 auto &key
= it
.first
;
1038 auto &instance_counters
= it
.second
;
1039 f
.open_object_section("i");
1040 f
.open_array_section("k");
1041 for (auto &sub_key
: key
) {
1042 f
.open_array_section("s");
1043 for (size_t i
= 0; i
< sub_key
.size(); i
++) {
1044 f
.dump_string(stringify(i
).c_str(), sub_key
[i
]);
1046 f
.close_section(); // s
1048 f
.close_section(); // k
1049 f
.open_array_section("c");
1050 for (auto &c
: instance_counters
) {
1051 f
.open_array_section("p");
1052 f
.dump_unsigned("0", c
.first
);
1053 f
.dump_unsigned("1", c
.second
);
1054 f
.close_section(); // p
1056 f
.close_section(); // c
1057 f
.close_section(); // i
1059 f
.close_section(); // counters
1064 void ActivePyModules::cluster_log(const std::string
&channel
, clog_type prio
,
1065 const std::string
&message
)
1067 std::lock_guard
l(lock
);
1069 if (channel
== "audit") {
1070 audit_clog
->do_log(prio
, message
);
1072 clog
->do_log(prio
, message
);