]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/ActivePyModules.cc
update download target update for octopus release
[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
7c673cae
FG
17#include "common/errno.h"
18#include "include/stringify.h"
19
20#include "PyFormatter.h"
21
22#include "osd/OSDMap.h"
23#include "mon/MonMap.h"
24
25#include "mgr/MgrContext.h"
26
3efd9988 27// For ::config_prefix
11fdf7f2 28#include "PyModule.h"
3efd9988
FG
29#include "PyModuleRegistry.h"
30
31#include "ActivePyModules.h"
11fdf7f2 32#include "DaemonServer.h"
7c673cae
FG
33
34#define dout_context g_ceph_context
35#define dout_subsys ceph_subsys_mgr
7c673cae 36#undef dout_prefix
11fdf7f2 37#define dout_prefix *_dout << "mgr " << __func__ << " "
7c673cae 38
11fdf7f2
TL
39ActivePyModules::ActivePyModules(PyModuleConfig &module_config_,
40 std::map<std::string, std::string> store_data,
3efd9988 41 DaemonStateIndex &ds, ClusterState &cs,
11fdf7f2
TL
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),
81eedcae 49 cmd_finisher(g_ceph_context, "cmd_finisher", "cmdfin"),
11fdf7f2
TL
50 server(server), py_module_registry(pmr), lock("ActivePyModules")
51{
52 store_cache = std::move(store_data);
81eedcae 53 cmd_finisher.start();
11fdf7f2 54}
7c673cae 55
3efd9988 56ActivePyModules::~ActivePyModules() = default;
7c673cae 57
3efd9988 58void ActivePyModules::dump_server(const std::string &hostname,
7c673cae
FG
59 const DaemonStateCollection &dmc,
60 Formatter *f)
61{
62 f->dump_string("hostname", hostname);
63 f->open_array_section("services");
64 std::string ceph_version;
65
66 for (const auto &i : dmc) {
11fdf7f2 67 std::lock_guard l(i.second->lock);
7c673cae 68 const auto &key = i.first;
224ce89b 69 const std::string &str_type = key.first;
7c673cae
FG
70 const std::string &svc_name = key.second;
71
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");
78 }
79
80 f->open_object_section("service");
81 f->dump_string("type", str_type);
82 f->dump_string("id", svc_name);
83 f->close_section();
84 }
85 f->close_section();
86
87 f->dump_string("ceph_version", ceph_version);
88}
89
90
91
3efd9988 92PyObject *ActivePyModules::get_server_python(const std::string &hostname)
7c673cae
FG
93{
94 PyThreadState *tstate = PyEval_SaveThread();
11fdf7f2 95 std::lock_guard l(lock);
7c673cae
FG
96 PyEval_RestoreThread(tstate);
97 dout(10) << " (" << hostname << ")" << dendl;
98
99 auto dmc = daemon_state.get_by_server(hostname);
100
101 PyFormatter f;
102 dump_server(hostname, dmc, &f);
103 return f.get();
104}
105
106
3efd9988 107PyObject *ActivePyModules::list_servers_python()
7c673cae 108{
11fdf7f2 109 PyFormatter f(false, true);
7c673cae 110 PyThreadState *tstate = PyEval_SaveThread();
7c673cae
FG
111 dout(10) << " >" << dendl;
112
11fdf7f2 113 daemon_state.with_daemons_by_server([this, &f, &tstate]
3efd9988 114 (const std::map<std::string, DaemonStateCollection> &all) {
11fdf7f2
TL
115 PyEval_RestoreThread(tstate);
116
3efd9988
FG
117 for (const auto &i : all) {
118 const auto &hostname = i.first;
7c673cae 119
3efd9988
FG
120 f.open_object_section("server");
121 dump_server(hostname, i.second, &f);
122 f.close_section();
123 }
124 });
7c673cae
FG
125
126 return f.get();
127}
128
3efd9988
FG
129PyObject *ActivePyModules::get_metadata_python(
130 const std::string &svc_type,
224ce89b 131 const std::string &svc_id)
7c673cae 132{
3efd9988
FG
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;
136 Py_RETURN_NONE;
137 }
138
11fdf7f2 139 std::lock_guard l(metadata->lock);
7c673cae
FG
140 PyFormatter f;
141 f.dump_string("hostname", metadata->hostname);
142 for (const auto &i : metadata->metadata) {
143 f.dump_string(i.first.c_str(), i.second);
144 }
145
146 return f.get();
147}
148
3efd9988
FG
149PyObject *ActivePyModules::get_daemon_status_python(
150 const std::string &svc_type,
224ce89b
WB
151 const std::string &svc_id)
152{
3efd9988
FG
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;
156 Py_RETURN_NONE;
157 }
158
11fdf7f2 159 std::lock_guard l(metadata->lock);
224ce89b
WB
160 PyFormatter f;
161 for (const auto &i : metadata->service_status) {
162 f.dump_string(i.first.c_str(), i.second);
163 }
164 return f.get();
165}
7c673cae 166
3efd9988 167PyObject *ActivePyModules::get_python(const std::string &what)
7c673cae 168{
11fdf7f2
TL
169 PyFormatter f;
170
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.
7c673cae 174 PyThreadState *tstate = PyEval_SaveThread();
7c673cae
FG
175
176 if (what == "fs_map") {
11fdf7f2
TL
177 cluster_state.with_fsmap([&f, &tstate](const FSMap &fsmap) {
178 PyEval_RestoreThread(tstate);
7c673cae
FG
179 fsmap.dump(&f);
180 });
181 return f.get();
182 } else if (what == "osdmap_crush_map_text") {
183 bufferlist rdata;
11fdf7f2
TL
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);
7c673cae
FG
187 });
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") {
11fdf7f2
TL
191 cluster_state.with_osdmap([&f, &what, &tstate](const OSDMap &osd_map){
192 PyEval_RestoreThread(tstate);
7c673cae
FG
193 if (what == "osd_map") {
194 osd_map.dump(&f);
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);
199 }
200 });
201 return f.get();
eafe8130
TL
202 } else if (what == "modified_config_options") {
203 PyEval_RestoreThread(tstate);
204 auto all_daemons = daemon_state.get_all();
205 set<string> names;
206 for (auto& [key, daemon] : all_daemons) {
207 std::lock_guard l(daemon->lock);
208 for (auto& [name, valmap] : daemon->config) {
209 names.insert(name);
210 }
211 }
212 f.open_array_section("options");
213 for (auto& name : names) {
214 f.dump_string("name", name);
215 }
216 f.close_section();
217 return f.get();
1adf2230 218 } else if (what.substr(0, 6) == "config") {
11fdf7f2 219 PyEval_RestoreThread(tstate);
1adf2230 220 if (what == "config_options") {
11fdf7f2 221 g_conf().config_options(&f);
1adf2230 222 } else if (what == "config") {
11fdf7f2 223 g_conf().show_config(&f);
1adf2230 224 }
7c673cae
FG
225 return f.get();
226 } else if (what == "mon_map") {
7c673cae 227 cluster_state.with_monmap(
11fdf7f2
TL
228 [&f, &tstate](const MonMap &monmap) {
229 PyEval_RestoreThread(tstate);
7c673cae
FG
230 monmap.dump(&f);
231 }
232 );
233 return f.get();
224ce89b 234 } else if (what == "service_map") {
224ce89b 235 cluster_state.with_servicemap(
11fdf7f2
TL
236 [&f, &tstate](const ServiceMap &service_map) {
237 PyEval_RestoreThread(tstate);
224ce89b
WB
238 service_map.dump(&f);
239 }
240 );
241 return f.get();
7c673cae 242 } else if (what == "osd_metadata") {
224ce89b 243 auto dmc = daemon_state.get_by_service("osd");
11fdf7f2
TL
244 PyEval_RestoreThread(tstate);
245
7c673cae 246 for (const auto &i : dmc) {
11fdf7f2 247 std::lock_guard l(i.second->lock);
7c673cae
FG
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);
252 }
253 f.close_section();
254 }
255 return f.get();
256 } else if (what == "pg_summary") {
7c673cae 257 cluster_state.with_pgmap(
11fdf7f2
TL
258 [&f, &tstate](const PGMap &pg_map) {
259 PyEval_RestoreThread(tstate);
260
7c673cae
FG
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]++;
271 }
272 all[state]++;
273 }
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);
279 }
280 f.close_section();
281 }
282 f.close_section();
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);
288 }
289 f.close_section();
290 }
291 f.close_section();
292 f.open_object_section("all");
293 for (const auto &i : all) {
294 f.dump_int(i.first.c_str(), i.second);
295 }
296 f.close_section();
28e407b8
AA
297 f.open_object_section("pg_stats_sum");
298 pg_map.pg_sum.dump(&f);
299 f.close_section();
7c673cae
FG
300 }
301 );
302 return f.get();
3efd9988 303 } else if (what == "pg_status") {
3efd9988 304 cluster_state.with_pgmap(
11fdf7f2
TL
305 [&f, &tstate](const PGMap &pg_map) {
306 PyEval_RestoreThread(tstate);
3efd9988
FG
307 pg_map.print_summary(&f, nullptr);
308 }
309 );
310 return f.get();
311 } else if (what == "pg_dump") {
11fdf7f2
TL
312 cluster_state.with_pgmap(
313 [&f, &tstate](const PGMap &pg_map) {
314 PyEval_RestoreThread(tstate);
315 pg_map.dump(&f);
316 }
317 );
318 return f.get();
319 } else if (what == "devices") {
320 daemon_state.with_devices2(
321 [&tstate, &f]() {
322 PyEval_RestoreThread(tstate);
323 f.open_array_section("devices");
324 },
325 [&f] (const DeviceState& dev) {
326 f.dump_object("device", dev);
327 });
328 f.close_section();
329 return f.get();
330 } else if (what.size() > 7 &&
331 what.substr(0, 7) == "device ") {
332 string devid = what.substr(7);
eafe8130
TL
333 if (!daemon_state.with_device(
334 devid,
335 [&f, &tstate] (const DeviceState& dev) {
336 PyEval_RestoreThread(tstate);
337 f.dump_object("device", dev);
338 })) {
339 // device not found
340 PyEval_RestoreThread(tstate);
341 }
11fdf7f2
TL
342 return f.get();
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);
348 }
3efd9988
FG
349 );
350 return f.get();
7c673cae 351 } else if (what == "df") {
11fdf7f2
TL
352 cluster_state.with_osdmap_and_pgmap(
353 [this, &f, &tstate](
354 const OSDMap& osd_map,
355 const PGMap &pg_map) {
356 PyEval_RestoreThread(tstate);
357 pg_map.dump_cluster_stats(nullptr, &f, true);
31f18b77 358 pg_map.dump_pool_stats_full(osd_map, nullptr, &f, true);
7c673cae 359 });
7c673cae
FG
360 return f.get();
361 } else if (what == "osd_stats") {
7c673cae 362 cluster_state.with_pgmap(
11fdf7f2
TL
363 [&f, &tstate](const PGMap &pg_map) {
364 PyEval_RestoreThread(tstate);
ded94939 365 pg_map.dump_osd_stats(&f, false);
7c673cae
FG
366 });
367 return f.get();
11fdf7f2
TL
368 } else if (what == "osd_pool_stats") {
369 int64_t poolid = -ENOENT;
370 string pool_name;
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()) {
376 poolid = p.first;
377 pg_map.dump_pool_stats_and_io_rate(poolid, osdmap, &f, nullptr);
378 }
379 f.close_section();
380 });
381 return f.get();
7c673cae 382 } else if (what == "health" || what == "mon_status") {
7c673cae
FG
383 bufferlist json;
384 if (what == "health") {
385 json = cluster_state.get_health();
386 } else if (what == "mon_status") {
387 json = cluster_state.get_mon_status();
388 } else {
11fdf7f2 389 ceph_abort();
7c673cae 390 }
11fdf7f2
TL
391
392 PyEval_RestoreThread(tstate);
7c673cae
FG
393 f.dump_string("json", json.to_str());
394 return f.get();
224ce89b 395 } else if (what == "mgr_map") {
11fdf7f2
TL
396 cluster_state.with_mgrmap([&f, &tstate](const MgrMap &mgr_map) {
397 PyEval_RestoreThread(tstate);
224ce89b
WB
398 mgr_map.dump(&f);
399 });
400 return f.get();
7c673cae
FG
401 } else {
402 derr << "Python module requested unknown data '" << what << "'" << dendl;
11fdf7f2 403 PyEval_RestoreThread(tstate);
7c673cae
FG
404 Py_RETURN_NONE;
405 }
406}
407
11fdf7f2 408void ActivePyModules::start_one(PyModuleRef py_module)
7c673cae 409{
11fdf7f2
TL
410 std::lock_guard l(lock);
411
412 ceph_assert(modules.count(py_module->get_name()) == 0);
413
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();
417
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);
422 if (r != 0) {
423 derr << "Failed to run module in active mode ('" << name << "')"
424 << dendl;
425 std::lock_guard l(lock);
426 modules.erase(name);
427 } else {
428 dout(4) << "Starting thread for " << name << dendl;
429 active_module->thread.create(active_module->get_thread_name());
430 }
431 }));
7c673cae
FG
432}
433
3efd9988 434void ActivePyModules::shutdown()
7c673cae 435{
11fdf7f2 436 std::lock_guard locker(lock);
7c673cae
FG
437
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;
3efd9988 442
31f18b77 443 lock.Unlock();
3efd9988 444 dout(10) << "calling module " << name << " shutdown()" << dendl;
7c673cae 445 module->shutdown();
3efd9988 446 dout(10) << "module " << name << " shutdown() returned" << dendl;
31f18b77 447 lock.Lock();
7c673cae
FG
448 }
449
450 // For modules implementing serve(), finish the threads where we
451 // were running that.
3efd9988 452 for (auto &i : modules) {
7c673cae 453 lock.Unlock();
3efd9988
FG
454 dout(10) << "joining module " << i.first << dendl;
455 i.second->thread.join();
456 dout(10) << "joined module " << i.first << dendl;
7c673cae
FG
457 lock.Lock();
458 }
7c673cae 459
81eedcae
TL
460 cmd_finisher.wait_for_empty();
461 cmd_finisher.stop();
462
7c673cae 463 modules.clear();
7c673cae
FG
464}
465
3efd9988 466void ActivePyModules::notify_all(const std::string &notify_type,
7c673cae
FG
467 const std::string &notify_id)
468{
11fdf7f2 469 std::lock_guard l(lock);
7c673cae
FG
470
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);
478 }));
479 }
480}
481
3efd9988 482void ActivePyModules::notify_all(const LogEntry &log_entry)
7c673cae 483{
11fdf7f2 484 std::lock_guard l(lock);
7c673cae
FG
485
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.
491 //
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);
497 }));
498 }
499}
500
11fdf7f2 501bool ActivePyModules::get_store(const std::string &module_name,
7c673cae
FG
502 const std::string &key, std::string *val) const
503{
11fdf7f2
TL
504 PyThreadState *tstate = PyEval_SaveThread();
505 std::lock_guard l(lock);
506 PyEval_RestoreThread(tstate);
507
508 const std::string global_key = PyModule::config_prefix
3efd9988 509 + module_name + "/" + key;
31f18b77 510
11fdf7f2 511 dout(4) << __func__ << " key: " << global_key << dendl;
7c673cae 512
11fdf7f2
TL
513 auto i = store_cache.find(global_key);
514 if (i != store_cache.end()) {
515 *val = i->second;
516 return true;
517 } else {
518 return false;
519 }
520}
521
522PyObject *ActivePyModules::dispatch_remote(
523 const std::string &other_module,
524 const std::string &method,
525 PyObject *args,
526 PyObject *kwargs,
527 std::string *err)
528{
529 auto mod_iter = modules.find(other_module);
530 ceph_assert(mod_iter != modules.end());
531
532 return mod_iter->second->dispatch_remote(method, args, kwargs, err);
533}
a8e16298 534
11fdf7f2
TL
535bool ActivePyModules::get_config(const std::string &module_name,
536 const std::string &key, std::string *val) const
537{
538 const std::string global_key = PyModule::config_prefix
539 + module_name + "/" + key;
540
541 dout(4) << __func__ << " key: " << global_key << dendl;
542
543 std::lock_guard lock(module_config.lock);
544
545 auto i = module_config.config.find(global_key);
546 if (i != module_config.config.end()) {
547 *val = i->second;
7c673cae
FG
548 return true;
549 } else {
550 return false;
551 }
552}
553
11fdf7f2
TL
554PyObject *ActivePyModules::get_typed_config(
555 const std::string &module_name,
556 const std::string &key,
557 const std::string &prefix) const
558{
559 PyThreadState *tstate = PyEval_SaveThread();
560 std::string value;
561 std::string final_key;
562 bool found = false;
563 if (prefix.size()) {
564 final_key = prefix + "/" + key;
565 found = get_config(module_name, final_key, &value);
566 }
567 if (!found) {
568 final_key = key;
569 found = get_config(module_name, final_key, &value);
570 }
571 if (found) {
572 PyModuleRef module = py_module_registry.get_module(module_name);
573 PyEval_RestoreThread(tstate);
574 if (!module) {
575 derr << "Module '" << module_name << "' is not available" << dendl;
576 Py_RETURN_NONE;
577 }
578 dout(10) << __func__ << " " << final_key << " found: " << value << dendl;
579 return module->get_typed_option_value(key, value);
580 }
581 PyEval_RestoreThread(tstate);
582 if (prefix.size()) {
583 dout(4) << __func__ << " [" << prefix << "/]" << key << " not found "
584 << dendl;
585 } else {
586 dout(4) << __func__ << " " << key << " not found " << dendl;
587 }
588 Py_RETURN_NONE;
589}
590
591PyObject *ActivePyModules::get_store_prefix(const std::string &module_name,
31f18b77
FG
592 const std::string &prefix) const
593{
594 PyThreadState *tstate = PyEval_SaveThread();
11fdf7f2
TL
595 std::lock_guard l(lock);
596 std::lock_guard lock(module_config.lock);
31f18b77
FG
597 PyEval_RestoreThread(tstate);
598
11fdf7f2 599 const std::string base_prefix = PyModule::config_prefix
3efd9988 600 + module_name + "/";
31f18b77 601 const std::string global_prefix = base_prefix + prefix;
11fdf7f2 602 dout(4) << __func__ << " prefix: " << global_prefix << dendl;
31f18b77
FG
603
604 PyFormatter f;
11fdf7f2
TL
605
606 for (auto p = store_cache.lower_bound(global_prefix);
607 p != store_cache.end() && p->first.find(global_prefix) == 0;
31f18b77
FG
608 ++p) {
609 f.dump_string(p->first.c_str() + base_prefix.size(), p->second);
610 }
611 return f.get();
612}
613
11fdf7f2 614void ActivePyModules::set_store(const std::string &module_name,
d2e6a577 615 const std::string &key, const boost::optional<std::string>& val)
7c673cae 616{
11fdf7f2 617 const std::string global_key = PyModule::config_prefix
3efd9988 618 + module_name + "/" + key;
11fdf7f2 619
7c673cae
FG
620 Command set_cmd;
621 {
11fdf7f2 622 std::lock_guard l(lock);
d2e6a577 623 if (val) {
11fdf7f2 624 store_cache[global_key] = *val;
d2e6a577 625 } else {
11fdf7f2 626 store_cache.erase(global_key);
d2e6a577 627 }
7c673cae
FG
628
629 std::ostringstream cmd_json;
7c673cae
FG
630 JSONFormatter jf;
631 jf.open_object_section("cmd");
d2e6a577
FG
632 if (val) {
633 jf.dump_string("prefix", "config-key set");
634 jf.dump_string("key", global_key);
635 jf.dump_string("val", *val);
636 } else {
637 jf.dump_string("prefix", "config-key del");
638 jf.dump_string("key", global_key);
639 }
7c673cae
FG
640 jf.close_section();
641 jf.flush(cmd_json);
7c673cae
FG
642 set_cmd.run(&monc, cmd_json.str());
643 }
644 set_cmd.wait();
645
646 if (set_cmd.r != 0) {
c07f9fc5 647 // config-key set will fail if mgr's auth key has insufficient
7c673cae
FG
648 // permission to set config keys
649 // FIXME: should this somehow raise an exception back into Python land?
c07f9fc5 650 dout(0) << "`config-key set " << global_key << " " << val << "` failed: "
7c673cae
FG
651 << cpp_strerror(set_cmd.r) << dendl;
652 dout(0) << "mon returned " << set_cmd.r << ": " << set_cmd.outs << dendl;
653 }
654}
655
11fdf7f2
TL
656void ActivePyModules::set_config(const std::string &module_name,
657 const std::string &key, const boost::optional<std::string>& val)
c07f9fc5 658{
11fdf7f2 659 module_config.set_config(&monc, module_name, key, val);
c07f9fc5
FG
660}
661
3efd9988 662std::map<std::string, std::string> ActivePyModules::get_services() const
7c673cae 663{
3efd9988 664 std::map<std::string, std::string> result;
11fdf7f2 665 std::lock_guard l(lock);
3efd9988
FG
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;
671 }
672 }
7c673cae 673
3efd9988 674 return result;
7c673cae
FG
675}
676
11fdf7f2
TL
677PyObject* ActivePyModules::with_perf_counters(
678 std::function<void(PerfCounterInstance& counter_instance, PerfCounterType& counter_type, PyFormatter& f)> fct,
224ce89b 679 const std::string &svc_name,
7c673cae 680 const std::string &svc_id,
11fdf7f2 681 const std::string &path) const
7c673cae
FG
682{
683 PyThreadState *tstate = PyEval_SaveThread();
11fdf7f2 684 std::lock_guard l(lock);
7c673cae
FG
685 PyEval_RestoreThread(tstate);
686
687 PyFormatter f;
688 f.open_array_section(path.c_str());
689
224ce89b 690 auto metadata = daemon_state.get(DaemonKey(svc_name, svc_id));
7c673cae 691 if (metadata) {
11fdf7f2 692 std::lock_guard l2(metadata->lock);
7c673cae
FG
693 if (metadata->perf_counters.instances.count(path)) {
694 auto counter_instance = metadata->perf_counters.instances.at(path);
28e407b8 695 auto counter_type = metadata->perf_counters.types.at(path);
11fdf7f2 696 fct(counter_instance, counter_type, f);
7c673cae
FG
697 } else {
698 dout(4) << "Missing counter: '" << path << "' ("
11fdf7f2 699 << svc_name << "." << svc_id << ")" << dendl;
7c673cae
FG
700 dout(20) << "Paths are:" << dendl;
701 for (const auto &i : metadata->perf_counters.instances) {
702 dout(20) << i.first << dendl;
703 }
704 }
705 } else {
706 dout(4) << "No daemon state for "
11fdf7f2 707 << svc_name << "." << svc_id << ")" << dendl;
7c673cae
FG
708 }
709 f.close_section();
710 return f.get();
711}
712
11fdf7f2
TL
713PyObject* ActivePyModules::get_counter_python(
714 const std::string &svc_name,
715 const std::string &svc_id,
716 const std::string &path)
717{
718 auto extract_counters = [](
719 PerfCounterInstance& counter_instance,
720 PerfCounterType& counter_type,
721 PyFormatter& f)
722 {
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);
730 f.close_section();
731 }
732 } else {
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);
738 f.close_section();
739 }
740 }
741 };
742 return with_perf_counters(extract_counters, svc_name, svc_id, path);
743}
744
745PyObject* ActivePyModules::get_latest_counter_python(
746 const std::string &svc_name,
747 const std::string &svc_id,
748 const std::string &path)
749{
750 auto extract_latest_counters = [](
751 PerfCounterInstance& counter_instance,
752 PerfCounterType& counter_type,
753 PyFormatter& f)
754 {
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);
760 } else {
761 const auto &datapoint = counter_instance.get_latest_data();
762 f.dump_unsigned("t", datapoint.t.sec());
763 f.dump_unsigned("v", datapoint.v);
764 }
765 };
766 return with_perf_counters(extract_latest_counters, svc_name, svc_id, path);
767}
768
3efd9988 769PyObject* ActivePyModules::get_perf_schema_python(
11fdf7f2 770 const std::string &svc_type,
c07f9fc5
FG
771 const std::string &svc_id)
772{
773 PyThreadState *tstate = PyEval_SaveThread();
11fdf7f2 774 std::lock_guard l(lock);
c07f9fc5
FG
775 PyEval_RestoreThread(tstate);
776
3efd9988 777 DaemonStateCollection daemons;
c07f9fc5
FG
778
779 if (svc_type == "") {
11fdf7f2 780 daemons = daemon_state.get_all();
c07f9fc5 781 } else if (svc_id.empty()) {
11fdf7f2 782 daemons = daemon_state.get_by_service(svc_type);
c07f9fc5
FG
783 } else {
784 auto key = DaemonKey(svc_type, svc_id);
785 // so that the below can be a loop in all cases
3efd9988
FG
786 auto got = daemon_state.get(key);
787 if (got != nullptr) {
788 daemons[key] = got;
c07f9fc5
FG
789 }
790 }
791
792 PyFormatter f;
3efd9988
FG
793 if (!daemons.empty()) {
794 for (auto statepair : daemons) {
c07f9fc5
FG
795 auto key = statepair.first;
796 auto state = statepair.second;
3efd9988
FG
797
798 std::ostringstream daemon_name;
c07f9fc5
FG
799 daemon_name << key.first << "." << key.second;
800 f.open_object_section(daemon_name.str().c_str());
801
11fdf7f2 802 std::lock_guard l(state->lock);
3efd9988
FG
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];
c07f9fc5
FG
807 f.dump_string("description", type.description);
808 if (!type.nick.empty()) {
809 f.dump_string("nick", type.nick);
810 }
811 f.dump_unsigned("type", type.type);
3efd9988 812 f.dump_unsigned("priority", type.priority);
1adf2230 813 f.dump_unsigned("units", type.unit);
c07f9fc5
FG
814 f.close_section();
815 }
816 f.close_section();
817 }
818 } else {
819 dout(4) << __func__ << ": No daemon state found for "
820 << svc_type << "." << svc_id << ")" << dendl;
821 }
c07f9fc5
FG
822 return f.get();
823}
824
3efd9988 825PyObject *ActivePyModules::get_context()
7c673cae
FG
826{
827 PyThreadState *tstate = PyEval_SaveThread();
11fdf7f2 828 std::lock_guard l(lock);
7c673cae
FG
829 PyEval_RestoreThread(tstate);
830
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);
835 return capsule;
836}
837
3efd9988
FG
838/**
839 * Helper for our wrapped types that take a capsule in their constructor.
840 */
841PyObject *construct_with_capsule(
842 const std::string &module_name,
843 const std::string &clsname,
844 void *wrapped)
224ce89b 845{
3efd9988
FG
846 // Look up the OSDMap type which we will construct
847 PyObject *module = PyImport_ImportModule(module_name.c_str());
848 if (!module) {
849 derr << "Failed to import python module:" << dendl;
850 derr << handle_pyerror() << dendl;
224ce89b 851 }
11fdf7f2 852 ceph_assert(module);
3efd9988
FG
853
854 PyObject *wrapper_type = PyObject_GetAttrString(
855 module, (const char*)clsname.c_str());
856 if (!wrapper_type) {
857 derr << "Failed to get python type:" << dendl;
858 derr << handle_pyerror() << dendl;
224ce89b 859 }
11fdf7f2 860 ceph_assert(wrapper_type);
3efd9988
FG
861
862 // Construct a capsule containing an OSDMap.
863 auto wrapped_capsule = PyCapsule_New(wrapped, nullptr, nullptr);
11fdf7f2 864 ceph_assert(wrapped_capsule);
3efd9988
FG
865
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;
872 }
11fdf7f2 873 ceph_assert(wrapper_instance != nullptr);
3efd9988
FG
874 Py_DECREF(pArgs);
875 Py_DECREF(wrapped_capsule);
876
877 Py_DECREF(wrapper_type);
878 Py_DECREF(module);
879
880 return wrapper_instance;
224ce89b
WB
881}
882
3efd9988 883PyObject *ActivePyModules::get_osdmap()
224ce89b 884{
3efd9988
FG
885 OSDMap *newmap = new OSDMap;
886
11fdf7f2
TL
887 PyThreadState *tstate = PyEval_SaveThread();
888 {
889 std::lock_guard l(lock);
890 cluster_state.with_osdmap([&](const OSDMap& o) {
891 newmap->deepish_copy_from(o);
892 });
893 }
894 PyEval_RestoreThread(tstate);
3efd9988
FG
895
896 return construct_with_capsule("mgr_module", "OSDMap", (void*)newmap);
224ce89b 897}
c07f9fc5 898
3efd9988 899void ActivePyModules::set_health_checks(const std::string& module_name,
c07f9fc5
FG
900 health_check_map_t&& checks)
901{
11fdf7f2
TL
902 bool changed = false;
903
904 lock.Lock();
3efd9988 905 auto p = modules.find(module_name);
c07f9fc5 906 if (p != modules.end()) {
11fdf7f2 907 changed = p->second->set_health_checks(std::move(checks));
c07f9fc5 908 }
11fdf7f2
TL
909 lock.Unlock();
910
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
914 // lockdep happy:
915 //
916 // send_report callers: DaemonServer::lock -> PyModuleRegistery::lock
917 // active_start: PyModuleRegistry::lock -> ActivePyModules::lock
918 //
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.
923 if (changed)
924 server.schedule_tick(0);
925}
926
927int ActivePyModules::handle_command(
92f5a8d4
TL
928 const ModuleCommand& module_command,
929 const MgrSession& session,
11fdf7f2
TL
930 const cmdmap_t &cmdmap,
931 const bufferlist &inbuf,
932 std::stringstream *ds,
933 std::stringstream *ss)
934{
92f5a8d4
TL
935 lock.lock();
936 auto mod_iter = modules.find(module_command.module_name);
11fdf7f2 937 if (mod_iter == modules.end()) {
92f5a8d4
TL
938 *ss << "Module '" << module_command.module_name << "' is not available";
939 lock.unlock();
11fdf7f2
TL
940 return -ENOENT;
941 }
942
92f5a8d4
TL
943 lock.unlock();
944 return mod_iter->second->handle_command(module_command, session, cmdmap,
945 inbuf, ds, ss);
c07f9fc5
FG
946}
947
3efd9988 948void ActivePyModules::get_health_checks(health_check_map_t *checks)
c07f9fc5 949{
11fdf7f2 950 std::lock_guard l(lock);
c07f9fc5
FG
951 for (auto& p : modules) {
952 p.second->get_health_checks(checks);
953 }
954}
3efd9988 955
11fdf7f2
TL
956void ActivePyModules::update_progress_event(
957 const std::string& evid,
958 const std::string& desc,
959 float progress)
960{
961 std::lock_guard l(lock);
962 auto& pe = progress_events[evid];
963 pe.message = desc;
964 pe.progress = progress;
965}
966
967void ActivePyModules::complete_progress_event(const std::string& evid)
968{
969 std::lock_guard l(lock);
970 progress_events.erase(evid);
971}
972
973void ActivePyModules::clear_all_progress_events()
974{
975 std::lock_guard l(lock);
976 progress_events.clear();
977}
978
979void ActivePyModules::get_progress_events(std::map<std::string,ProgressEvent> *events)
980{
981 std::lock_guard l(lock);
982 *events = progress_events;
983}
984
985void ActivePyModules::config_notify()
986{
987 std::lock_guard l(lock);
988 for (auto& i : modules) {
989 auto module = i.second.get();
990 // Send all python calls down a Finisher to avoid blocking
991 // C++ code, and avoid any potential lock cycles.
992 finisher.queue(new FunctionContext([module](int r){
993 module->config_notify();
994 }));
995 }
996}
997
3efd9988
FG
998void ActivePyModules::set_uri(const std::string& module_name,
999 const std::string &uri)
1000{
11fdf7f2 1001 std::lock_guard l(lock);
3efd9988
FG
1002
1003 dout(4) << " module " << module_name << " set URI '" << uri << "'" << dendl;
1004
1005 modules[module_name]->set_uri(uri);
1006}
1007
11fdf7f2
TL
1008OSDPerfMetricQueryID ActivePyModules::add_osd_perf_query(
1009 const OSDPerfMetricQuery &query,
1010 const std::optional<OSDPerfMetricLimit> &limit)
1011{
1012 return server.add_osd_perf_query(query, limit);
1013}
1014
1015void ActivePyModules::remove_osd_perf_query(OSDPerfMetricQueryID query_id)
1016{
1017 int r = server.remove_osd_perf_query(query_id);
1018 if (r < 0) {
1019 dout(0) << "remove_osd_perf_query for query_id=" << query_id << " failed: "
1020 << cpp_strerror(r) << dendl;
1021 }
1022}
1023
1024PyObject *ActivePyModules::get_osd_perf_counters(OSDPerfMetricQueryID query_id)
1025{
1026 std::map<OSDPerfMetricKey, PerformanceCounters> counters;
1027
1028 int r = server.get_osd_perf_counters(query_id, &counters);
1029 if (r < 0) {
1030 dout(0) << "get_osd_perf_counters for query_id=" << query_id << " failed: "
1031 << cpp_strerror(r) << dendl;
1032 Py_RETURN_NONE;
1033 }
1034
1035 PyFormatter f;
1036
1037 f.open_array_section("counters");
1038 for (auto &it : counters) {
1039 auto &key = it.first;
1040 auto &instance_counters = it.second;
1041 f.open_object_section("i");
1042 f.open_array_section("k");
1043 for (auto &sub_key : key) {
1044 f.open_array_section("s");
1045 for (size_t i = 0; i < sub_key.size(); i++) {
1046 f.dump_string(stringify(i).c_str(), sub_key[i]);
1047 }
1048 f.close_section(); // s
1049 }
1050 f.close_section(); // k
1051 f.open_array_section("c");
1052 for (auto &c : instance_counters) {
1053 f.open_array_section("p");
1054 f.dump_unsigned("0", c.first);
1055 f.dump_unsigned("1", c.second);
1056 f.close_section(); // p
1057 }
1058 f.close_section(); // c
1059 f.close_section(); // i
1060 }
1061 f.close_section(); // counters
1062
1063 return f.get();
1064}
1065
1066void ActivePyModules::cluster_log(const std::string &channel, clog_type prio,
1067 const std::string &message)
1068{
1069 std::lock_guard l(lock);
1070
1071 if (channel == "audit") {
1072 audit_clog->do_log(prio, message);
1073 } else {
1074 clog->do_log(prio, message);
1075 }
1076}