]> git.proxmox.com Git - ceph.git/blob - ceph/src/mgr/BaseMgrModule.cc
import ceph quincy 17.2.6
[ceph.git] / ceph / src / mgr / BaseMgrModule.cc
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) 2016 John Spray <john.spray@redhat.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 /**
15 * The interface we present to python code that runs within
16 * ceph-mgr. This is implemented as a Python class from which
17 * all modules must inherit -- access to the Ceph state is then
18 * available as methods on that object.
19 */
20
21 #include "Python.h"
22
23 #include "Mgr.h"
24
25 #include "mon/MonClient.h"
26 #include "common/errno.h"
27 #include "common/version.h"
28 #include "mgr/Types.h"
29
30 #include "PyUtil.h"
31 #include "BaseMgrModule.h"
32 #include "Gil.h"
33
34 #include <algorithm>
35
36 #define dout_context g_ceph_context
37 #define dout_subsys ceph_subsys_mgr
38
39 #define PLACEHOLDER ""
40
41 using std::list;
42 using std::string;
43
44 typedef struct {
45 PyObject_HEAD
46 ActivePyModules *py_modules;
47 ActivePyModule *this_module;
48 } BaseMgrModule;
49
50 class MonCommandCompletion : public Context
51 {
52 ActivePyModules *py_modules;
53 PyObject *python_completion;
54 const std::string tag;
55 SafeThreadState pThreadState;
56
57 public:
58 std::string outs;
59 bufferlist outbl;
60
61 MonCommandCompletion(
62 ActivePyModules *py_modules_, PyObject* ev,
63 const std::string &tag_, PyThreadState *ts_)
64 : py_modules(py_modules_), python_completion(ev),
65 tag(tag_), pThreadState(ts_)
66 {
67 ceph_assert(python_completion != nullptr);
68 Py_INCREF(python_completion);
69 }
70
71 ~MonCommandCompletion() override
72 {
73 if (python_completion) {
74 // Usually do this in finish(): this path is only for if we're
75 // being destroyed without completing.
76 Gil gil(pThreadState, true);
77 Py_DECREF(python_completion);
78 python_completion = nullptr;
79 }
80 }
81
82 void finish(int r) override
83 {
84 ceph_assert(python_completion != nullptr);
85
86 dout(10) << "MonCommandCompletion::finish()" << dendl;
87 {
88 // Scoped so the Gil is released before calling notify_all()
89 // Create new thread state because this is called via the MonClient
90 // Finisher, not the PyModules finisher.
91 Gil gil(pThreadState, true);
92
93 auto set_fn = PyObject_GetAttrString(python_completion, "complete");
94 ceph_assert(set_fn != nullptr);
95
96 auto pyR = PyLong_FromLong(r);
97 auto pyOutBl = PyUnicode_FromString(outbl.to_str().c_str());
98 auto pyOutS = PyUnicode_FromString(outs.c_str());
99 auto args = PyTuple_Pack(3, pyR, pyOutBl, pyOutS);
100 Py_DECREF(pyR);
101 Py_DECREF(pyOutBl);
102 Py_DECREF(pyOutS);
103
104 auto rtn = PyObject_CallObject(set_fn, args);
105 if (rtn != nullptr) {
106 Py_DECREF(rtn);
107 }
108 Py_DECREF(args);
109 Py_DECREF(set_fn);
110
111 Py_DECREF(python_completion);
112 python_completion = nullptr;
113 }
114 py_modules->notify_all("command", tag);
115 }
116 };
117
118
119 static PyObject*
120 ceph_send_command(BaseMgrModule *self, PyObject *args)
121 {
122 // Like mon, osd, mds
123 char *type = nullptr;
124
125 // Like "23" for an OSD or "myid" for an MDS
126 char *name = nullptr;
127
128 char *cmd_json = nullptr;
129 char *tag = nullptr;
130 char *inbuf_ptr = nullptr;
131 Py_ssize_t inbuf_len = 0;
132 bufferlist inbuf = {};
133
134 PyObject *completion = nullptr;
135 if (!PyArg_ParseTuple(args, "Ossssz#:ceph_send_command",
136 &completion, &type, &name, &cmd_json, &tag, &inbuf_ptr, &inbuf_len)) {
137 return nullptr;
138 }
139
140 if (inbuf_ptr) {
141 inbuf.append(inbuf_ptr, (unsigned)inbuf_len);
142 }
143
144 auto set_fn = PyObject_GetAttrString(completion, "complete");
145 if (set_fn == nullptr) {
146 ceph_abort(); // TODO raise python exception instead
147 } else {
148 ceph_assert(PyCallable_Check(set_fn));
149 }
150 Py_DECREF(set_fn);
151
152 MonCommandCompletion *command_c = new MonCommandCompletion(self->py_modules,
153 completion, tag, PyThreadState_Get());
154
155 PyThreadState *tstate = PyEval_SaveThread();
156
157 if (std::string(type) == "mon") {
158
159 // Wait for the latest OSDMap after each command we send to
160 // the mons. This is a heavy-handed hack to make life simpler
161 // for python module authors, so that they know whenever they
162 // run a command they've gt a fresh OSDMap afterwards.
163 // TODO: enhance MCommand interface so that it returns
164 // latest cluster map versions on completion, and callers
165 // can wait for those.
166 auto c = new LambdaContext([command_c, self](int command_r){
167 self->py_modules->get_objecter().wait_for_latest_osdmap(
168 [command_c, command_r](boost::system::error_code) {
169 command_c->complete(command_r);
170 });
171 });
172
173 self->py_modules->get_monc().start_mon_command(
174 name,
175 {cmd_json},
176 inbuf,
177 &command_c->outbl,
178 &command_c->outs,
179 new C_OnFinisher(c, &self->py_modules->cmd_finisher));
180 } else if (std::string(type) == "osd") {
181 std::string err;
182 uint64_t osd_id = strict_strtoll(name, 10, &err);
183 if (!err.empty()) {
184 delete command_c;
185 string msg("invalid osd_id: ");
186 msg.append("\"").append(name).append("\"");
187 PyEval_RestoreThread(tstate);
188 PyErr_SetString(PyExc_ValueError, msg.c_str());
189 return nullptr;
190 }
191
192 ceph_tid_t tid;
193 self->py_modules->get_objecter().osd_command(
194 osd_id,
195 {cmd_json},
196 inbuf,
197 &tid,
198 [command_c, f = &self->py_modules->cmd_finisher]
199 (boost::system::error_code ec, std::string s, ceph::buffer::list bl) {
200 command_c->outs = std::move(s);
201 command_c->outbl = std::move(bl);
202 f->queue(command_c);
203 });
204 } else if (std::string(type) == "mds") {
205 int r = self->py_modules->get_client().mds_command(
206 name,
207 {cmd_json},
208 inbuf,
209 &command_c->outbl,
210 &command_c->outs,
211 new C_OnFinisher(command_c, &self->py_modules->cmd_finisher));
212 if (r != 0) {
213 string msg("failed to send command to mds: ");
214 msg.append(cpp_strerror(r));
215 PyEval_RestoreThread(tstate);
216 PyErr_SetString(PyExc_RuntimeError, msg.c_str());
217 return nullptr;
218 }
219 } else if (std::string(type) == "pg") {
220 pg_t pgid;
221 if (!pgid.parse(name)) {
222 delete command_c;
223 string msg("invalid pgid: ");
224 msg.append("\"").append(name).append("\"");
225 PyEval_RestoreThread(tstate);
226 PyErr_SetString(PyExc_ValueError, msg.c_str());
227 return nullptr;
228 }
229
230 ceph_tid_t tid;
231 self->py_modules->get_objecter().pg_command(
232 pgid,
233 {cmd_json},
234 inbuf,
235 &tid,
236 [command_c, f = &self->py_modules->cmd_finisher]
237 (boost::system::error_code ec, std::string s, ceph::buffer::list bl) {
238 command_c->outs = std::move(s);
239 command_c->outbl = std::move(bl);
240 f->queue(command_c);
241 });
242 PyEval_RestoreThread(tstate);
243 return nullptr;
244 } else {
245 delete command_c;
246 string msg("unknown service type: ");
247 msg.append(type);
248 PyEval_RestoreThread(tstate);
249 PyErr_SetString(PyExc_ValueError, msg.c_str());
250 return nullptr;
251 }
252
253 PyEval_RestoreThread(tstate);
254 Py_RETURN_NONE;
255 }
256
257 static PyObject*
258 ceph_set_health_checks(BaseMgrModule *self, PyObject *args)
259 {
260 PyObject *checks = NULL;
261 if (!PyArg_ParseTuple(args, "O:ceph_set_health_checks", &checks)) {
262 return NULL;
263 }
264 if (!PyDict_Check(checks)) {
265 derr << __func__ << " arg not a dict" << dendl;
266 Py_RETURN_NONE;
267 }
268 PyObject *checksls = PyDict_Items(checks);
269 health_check_map_t out_checks;
270 for (int i = 0; i < PyList_Size(checksls); ++i) {
271 PyObject *kv = PyList_GET_ITEM(checksls, i);
272 char *check_name = nullptr;
273 PyObject *check_info = nullptr;
274 if (!PyArg_ParseTuple(kv, "sO:pair", &check_name, &check_info)) {
275 derr << __func__ << " dict item " << i
276 << " not a size 2 tuple" << dendl;
277 continue;
278 }
279 if (!PyDict_Check(check_info)) {
280 derr << __func__ << " item " << i << " " << check_name
281 << " value not a dict" << dendl;
282 continue;
283 }
284 health_status_t severity = HEALTH_OK;
285 string summary;
286 list<string> detail;
287 int64_t count = 0;
288 PyObject *infols = PyDict_Items(check_info);
289 for (int j = 0; j < PyList_Size(infols); ++j) {
290 PyObject *pair = PyList_GET_ITEM(infols, j);
291 if (!PyTuple_Check(pair)) {
292 derr << __func__ << " item " << i << " pair " << j
293 << " not a tuple" << dendl;
294 continue;
295 }
296 char *k = nullptr;
297 PyObject *v = nullptr;
298 if (!PyArg_ParseTuple(pair, "sO:pair", &k, &v)) {
299 derr << __func__ << " item " << i << " pair " << j
300 << " not a size 2 tuple" << dendl;
301 continue;
302 }
303 string ks(k);
304 if (ks == "severity") {
305 if (!PyUnicode_Check(v)) {
306 derr << __func__ << " check " << check_name
307 << " severity value not string" << dendl;
308 continue;
309 }
310 if (const string vs = PyUnicode_AsUTF8(v); vs == "warning") {
311 severity = HEALTH_WARN;
312 } else if (vs == "error") {
313 severity = HEALTH_ERR;
314 }
315 } else if (ks == "summary") {
316 if (!PyUnicode_Check(v)) {
317 derr << __func__ << " check " << check_name
318 << " summary value not [unicode] string" << dendl;
319 continue;
320 } else {
321 summary = PyUnicode_AsUTF8(v);
322 }
323 } else if (ks == "count") {
324 if (PyLong_Check(v)) {
325 count = PyLong_AsLong(v);
326 } else {
327 derr << __func__ << " check " << check_name
328 << " count value not int" << dendl;
329 continue;
330 }
331 } else if (ks == "detail") {
332 if (!PyList_Check(v)) {
333 derr << __func__ << " check " << check_name
334 << " detail value not list" << dendl;
335 continue;
336 }
337 for (int k = 0; k < PyList_Size(v); ++k) {
338 PyObject *di = PyList_GET_ITEM(v, k);
339 if (!PyUnicode_Check(di)) {
340 derr << __func__ << " check " << check_name
341 << " detail item " << k << " not a [unicode] string" << dendl;
342 continue;
343 } else {
344 detail.push_back(PyUnicode_AsUTF8(di));
345 }
346 }
347 } else {
348 derr << __func__ << " check " << check_name
349 << " unexpected key " << k << dendl;
350 }
351 }
352 auto& d = out_checks.add(check_name, severity, summary, count);
353 d.detail.swap(detail);
354 }
355
356 JSONFormatter jf(true);
357 dout(10) << "module " << self->this_module->get_name()
358 << " health checks:\n";
359 out_checks.dump(&jf);
360 jf.flush(*_dout);
361 *_dout << dendl;
362 without_gil([&] {
363 self->py_modules->set_health_checks(self->this_module->get_name(),
364 std::move(out_checks));
365 });
366 Py_RETURN_NONE;
367 }
368
369
370 static PyObject*
371 ceph_state_get(BaseMgrModule *self, PyObject *args)
372 {
373 char *what = NULL;
374 if (!PyArg_ParseTuple(args, "s:ceph_state_get", &what)) {
375 return NULL;
376 }
377
378 return self->py_modules->cacheable_get_python(what);
379 }
380
381
382 static PyObject*
383 ceph_get_server(BaseMgrModule *self, PyObject *args)
384 {
385 char *hostname = NULL;
386 if (!PyArg_ParseTuple(args, "z:ceph_get_server", &hostname)) {
387 return NULL;
388 }
389
390 if (hostname) {
391 return self->py_modules->get_server_python(hostname);
392 } else {
393 return self->py_modules->list_servers_python();
394 }
395 }
396
397 static PyObject*
398 ceph_get_mgr_id(BaseMgrModule *self, PyObject *args)
399 {
400 return PyUnicode_FromString(g_conf()->name.get_id().c_str());
401 }
402
403 static PyObject*
404 ceph_option_get(BaseMgrModule *self, PyObject *args)
405 {
406 char *what = nullptr;
407 if (!PyArg_ParseTuple(args, "s:ceph_option_get", &what)) {
408 derr << "Invalid args!" << dendl;
409 return nullptr;
410 }
411
412 const Option *opt = g_conf().find_option(string(what));
413 if (opt) {
414 std::string value;
415 switch (int r = g_conf().get_val(string(what), &value); r) {
416 case -ENOMEM:
417 PyErr_NoMemory();
418 return nullptr;
419 case -ENAMETOOLONG:
420 PyErr_SetString(PyExc_ValueError, "value too long");
421 return nullptr;
422 default:
423 ceph_assert(r == 0);
424 break;
425 }
426 dout(10) << "ceph_option_get " << what << " found: " << value << dendl;
427 return get_python_typed_option_value(opt->type, value);
428 } else {
429 dout(4) << "ceph_option_get " << what << " not found " << dendl;
430 PyErr_Format(PyExc_KeyError, "option not found: %s", what);
431 return nullptr;
432 }
433 }
434
435 static PyObject*
436 ceph_foreign_option_get(BaseMgrModule *self, PyObject *args)
437 {
438 char *who = nullptr;
439 char *what = nullptr;
440 if (!PyArg_ParseTuple(args, "ss:ceph_foreign_option_get", &who, &what)) {
441 derr << "Invalid args!" << dendl;
442 return nullptr;
443 }
444 return self->py_modules->get_foreign_config(who, what);
445 }
446
447 static PyObject*
448 ceph_get_module_option(BaseMgrModule *self, PyObject *args)
449 {
450 char *module = nullptr;
451 char *key = nullptr;
452 char *prefix = nullptr;
453 if (!PyArg_ParseTuple(args, "ss|s:ceph_get_module_option", &module, &key,
454 &prefix)) {
455 derr << "Invalid args!" << dendl;
456 return nullptr;
457 }
458 std::string str_prefix;
459 if (prefix) {
460 str_prefix = prefix;
461 }
462 assert(self->this_module->py_module);
463 auto pResult = self->py_modules->get_typed_config(module, key, str_prefix);
464 return pResult;
465 }
466
467 static PyObject*
468 ceph_store_get_prefix(BaseMgrModule *self, PyObject *args)
469 {
470 char *prefix = nullptr;
471 if (!PyArg_ParseTuple(args, "s:ceph_store_get_prefix", &prefix)) {
472 derr << "Invalid args!" << dendl;
473 return nullptr;
474 }
475
476 return self->py_modules->get_store_prefix(self->this_module->get_name(),
477 prefix);
478 }
479
480 static PyObject*
481 ceph_set_module_option(BaseMgrModule *self, PyObject *args)
482 {
483 char *module = nullptr;
484 char *key = nullptr;
485 char *value = nullptr;
486 if (!PyArg_ParseTuple(args, "ssz:ceph_set_module_option",
487 &module, &key, &value)) {
488 derr << "Invalid args!" << dendl;
489 return nullptr;
490 }
491 std::optional<string> val;
492 if (value) {
493 val = value;
494 }
495 auto [ret, msg] = without_gil([&] {
496 return self->py_modules->set_config(module, key, val);
497 });
498 if (ret) {
499 PyErr_SetString(PyExc_ValueError, msg.c_str());
500 return nullptr;
501 }
502 Py_RETURN_NONE;
503 }
504
505 static PyObject*
506 ceph_store_get(BaseMgrModule *self, PyObject *args)
507 {
508 char *what = nullptr;
509 if (!PyArg_ParseTuple(args, "s:ceph_store_get", &what)) {
510 derr << "Invalid args!" << dendl;
511 return nullptr;
512 }
513
514 std::string value;
515 bool found = self->py_modules->get_store(self->this_module->get_name(),
516 what, &value);
517 if (found) {
518 dout(10) << "ceph_store_get " << what << " found: " << value.c_str() << dendl;
519 return PyUnicode_FromString(value.c_str());
520 } else {
521 dout(4) << "ceph_store_get " << what << " not found " << dendl;
522 Py_RETURN_NONE;
523 }
524 }
525
526 static PyObject*
527 ceph_store_set(BaseMgrModule *self, PyObject *args)
528 {
529 char *key = nullptr;
530 char *value = nullptr;
531 if (!PyArg_ParseTuple(args, "sz:ceph_store_set", &key, &value)) {
532 return nullptr;
533 }
534 std::optional<string> val;
535 if (value) {
536 val = value;
537 }
538 without_gil([&] {
539 self->py_modules->set_store(self->this_module->get_name(), key, val);
540 });
541 Py_RETURN_NONE;
542 }
543
544 static PyObject*
545 get_metadata(BaseMgrModule *self, PyObject *args)
546 {
547 char *svc_name = NULL;
548 char *svc_id = NULL;
549 if (!PyArg_ParseTuple(args, "ss:get_metadata", &svc_name, &svc_id)) {
550 return nullptr;
551 }
552 return self->py_modules->get_metadata_python(svc_name, svc_id);
553 }
554
555 static PyObject*
556 get_daemon_status(BaseMgrModule *self, PyObject *args)
557 {
558 char *svc_name = NULL;
559 char *svc_id = NULL;
560 if (!PyArg_ParseTuple(args, "ss:get_daemon_status", &svc_name,
561 &svc_id)) {
562 return nullptr;
563 }
564 return self->py_modules->get_daemon_status_python(svc_name, svc_id);
565 }
566
567 static PyObject*
568 ceph_log(BaseMgrModule *self, PyObject *args)
569 {
570 char *record = nullptr;
571 if (!PyArg_ParseTuple(args, "s:log", &record)) {
572 return nullptr;
573 }
574
575 ceph_assert(self->this_module);
576
577 self->this_module->log(record);
578
579 Py_RETURN_NONE;
580 }
581
582 static PyObject*
583 ceph_cluster_log(BaseMgrModule *self, PyObject *args)
584 {
585 int prio = 0;
586 char *channel = nullptr;
587 char *message = nullptr;
588
589 if (!PyArg_ParseTuple(args, "sis:ceph_cluster_log", &channel, &prio, &message)) {
590 return nullptr;
591 }
592 without_gil([&] {
593 self->py_modules->cluster_log(channel, (clog_type)prio, message);
594 });
595 Py_RETURN_NONE;
596 }
597
598 static PyObject *
599 ceph_get_version(BaseMgrModule *self, PyObject *args)
600 {
601 return PyUnicode_FromString(pretty_version_to_str().c_str());
602 }
603
604 static PyObject *
605 ceph_get_ceph_conf_path(BaseMgrModule *self, PyObject *args)
606 {
607 return PyUnicode_FromString(g_conf().get_conf_path().c_str());
608 }
609
610 static PyObject *
611 ceph_get_release_name(BaseMgrModule *self, PyObject *args)
612 {
613 return PyUnicode_FromString(ceph_release_to_str());
614 }
615
616 static PyObject *
617 ceph_lookup_release_name(BaseMgrModule *self, PyObject *args)
618 {
619 int major = 0;
620 if (!PyArg_ParseTuple(args, "i:ceph_lookup_release_name", &major)) {
621 return nullptr;
622 }
623 return PyUnicode_FromString(ceph_release_name(major));
624 }
625
626 static PyObject *
627 ceph_get_context(BaseMgrModule *self)
628 {
629 return self->py_modules->get_context();
630 }
631
632 static PyObject*
633 get_counter(BaseMgrModule *self, PyObject *args)
634 {
635 char *svc_name = nullptr;
636 char *svc_id = nullptr;
637 char *counter_path = nullptr;
638 if (!PyArg_ParseTuple(args, "sss:get_counter", &svc_name,
639 &svc_id, &counter_path)) {
640 return nullptr;
641 }
642 return self->py_modules->get_counter_python(
643 svc_name, svc_id, counter_path);
644 }
645
646 static PyObject*
647 get_latest_counter(BaseMgrModule *self, PyObject *args)
648 {
649 char *svc_name = nullptr;
650 char *svc_id = nullptr;
651 char *counter_path = nullptr;
652 if (!PyArg_ParseTuple(args, "sss:get_counter", &svc_name,
653 &svc_id, &counter_path)) {
654 return nullptr;
655 }
656 return self->py_modules->get_latest_counter_python(
657 svc_name, svc_id, counter_path);
658 }
659
660 static PyObject*
661 get_perf_schema(BaseMgrModule *self, PyObject *args)
662 {
663 char *type_str = nullptr;
664 char *svc_id = nullptr;
665 if (!PyArg_ParseTuple(args, "ss:get_perf_schema", &type_str,
666 &svc_id)) {
667 return nullptr;
668 }
669
670 return self->py_modules->get_perf_schema_python(type_str, svc_id);
671 }
672
673 static PyObject*
674 ceph_get_rocksdb_version(BaseMgrModule *self)
675 {
676 return self->py_modules->get_rocksdb_version();
677 }
678
679
680 static PyObject *
681 ceph_get_osdmap(BaseMgrModule *self, PyObject *args)
682 {
683 return self->py_modules->get_osdmap();
684 }
685
686 static PyObject*
687 ceph_set_uri(BaseMgrModule *self, PyObject *args)
688 {
689 char *svc_str = nullptr;
690 if (!PyArg_ParseTuple(args, "s:ceph_advertize_service",
691 &svc_str)) {
692 return nullptr;
693 }
694 // We call down into PyModules even though we have a MgrPyModule
695 // reference here, because MgrPyModule's fields are protected
696 // by PyModules' lock.
697 without_gil([&] {
698 self->py_modules->set_uri(self->this_module->get_name(), svc_str);
699 });
700 Py_RETURN_NONE;
701 }
702
703 static PyObject*
704 ceph_set_wear_level(BaseMgrModule *self, PyObject *args)
705 {
706 char *devid = nullptr;
707 float wear_level;
708 if (!PyArg_ParseTuple(args, "sf:ceph_set_wear_level",
709 &devid, &wear_level)) {
710 return nullptr;
711 }
712 without_gil([&] {
713 self->py_modules->set_device_wear_level(devid, wear_level);
714 });
715 Py_RETURN_NONE;
716 }
717
718 static PyObject*
719 ceph_have_mon_connection(BaseMgrModule *self, PyObject *args)
720 {
721 if (self->py_modules->get_monc().is_connected()) {
722 Py_RETURN_TRUE;
723 } else {
724 Py_RETURN_FALSE;
725 }
726 }
727
728 static PyObject*
729 ceph_update_progress_event(BaseMgrModule *self, PyObject *args)
730 {
731 char *evid = nullptr;
732 char *desc = nullptr;
733 float progress = 0.0;
734 bool add_to_ceph_s = false;
735 if (!PyArg_ParseTuple(args, "ssfb:ceph_update_progress_event",
736 &evid, &desc, &progress, &add_to_ceph_s)) {
737 return nullptr;
738 }
739 without_gil([&] {
740 self->py_modules->update_progress_event(evid, desc, progress, add_to_ceph_s);
741 });
742 Py_RETURN_NONE;
743 }
744
745 static PyObject*
746 ceph_complete_progress_event(BaseMgrModule *self, PyObject *args)
747 {
748 char *evid = nullptr;
749 if (!PyArg_ParseTuple(args, "s:ceph_complete_progress_event",
750 &evid)) {
751 return nullptr;
752 }
753 without_gil([&] {
754 self->py_modules->complete_progress_event(evid);
755 });
756 Py_RETURN_NONE;
757 }
758
759 static PyObject*
760 ceph_clear_all_progress_events(BaseMgrModule *self, PyObject *args)
761 {
762 without_gil([&] {
763 self->py_modules->clear_all_progress_events();
764 });
765 Py_RETURN_NONE;
766 }
767
768
769
770 static PyObject *
771 ceph_dispatch_remote(BaseMgrModule *self, PyObject *args)
772 {
773 char *other_module = nullptr;
774 char *method = nullptr;
775 PyObject *remote_args = nullptr;
776 PyObject *remote_kwargs = nullptr;
777 if (!PyArg_ParseTuple(args, "ssOO:ceph_dispatch_remote",
778 &other_module, &method, &remote_args, &remote_kwargs)) {
779 return nullptr;
780 }
781
782 // Early error handling, because if the module doesn't exist then we
783 // won't be able to use its thread state to set python error state
784 // inside dispatch_remote().
785 if (!self->py_modules->module_exists(other_module)) {
786 derr << "no module '" << other_module << "'" << dendl;
787 PyErr_SetString(PyExc_ImportError, "Module not found");
788 return nullptr;
789 }
790
791 // Drop GIL from calling python thread state, it will be taken
792 // both for checking for method existence and for executing method.
793 PyThreadState *tstate = PyEval_SaveThread();
794
795 if (!self->py_modules->method_exists(other_module, method)) {
796 PyEval_RestoreThread(tstate);
797 PyErr_SetString(PyExc_NameError, "Method not found");
798 return nullptr;
799 }
800
801 std::string err;
802 auto result = self->py_modules->dispatch_remote(other_module, method,
803 remote_args, remote_kwargs, &err);
804
805 PyEval_RestoreThread(tstate);
806
807 if (result == nullptr) {
808 std::stringstream ss;
809 ss << "Remote method threw exception: " << err;
810 PyErr_SetString(PyExc_RuntimeError, ss.str().c_str());
811 derr << ss.str() << dendl;
812 }
813
814 return result;
815 }
816
817 static PyObject*
818 ceph_add_osd_perf_query(BaseMgrModule *self, PyObject *args)
819 {
820 static const std::string NAME_KEY_DESCRIPTOR = "key_descriptor";
821 static const std::string NAME_COUNTERS_DESCRIPTORS =
822 "performance_counter_descriptors";
823 static const std::string NAME_LIMIT = "limit";
824 static const std::string NAME_SUB_KEY_TYPE = "type";
825 static const std::string NAME_SUB_KEY_REGEX = "regex";
826 static const std::string NAME_LIMIT_ORDER_BY = "order_by";
827 static const std::string NAME_LIMIT_MAX_COUNT = "max_count";
828 static const std::map<std::string, OSDPerfMetricSubKeyType> sub_key_types = {
829 {"client_id", OSDPerfMetricSubKeyType::CLIENT_ID},
830 {"client_address", OSDPerfMetricSubKeyType::CLIENT_ADDRESS},
831 {"pool_id", OSDPerfMetricSubKeyType::POOL_ID},
832 {"namespace", OSDPerfMetricSubKeyType::NAMESPACE},
833 {"osd_id", OSDPerfMetricSubKeyType::OSD_ID},
834 {"pg_id", OSDPerfMetricSubKeyType::PG_ID},
835 {"object_name", OSDPerfMetricSubKeyType::OBJECT_NAME},
836 {"snap_id", OSDPerfMetricSubKeyType::SNAP_ID},
837 };
838 static const std::map<std::string, PerformanceCounterType> counter_types = {
839 {"ops", PerformanceCounterType::OPS},
840 {"write_ops", PerformanceCounterType::WRITE_OPS},
841 {"read_ops", PerformanceCounterType::READ_OPS},
842 {"bytes", PerformanceCounterType::BYTES},
843 {"write_bytes", PerformanceCounterType::WRITE_BYTES},
844 {"read_bytes", PerformanceCounterType::READ_BYTES},
845 {"latency", PerformanceCounterType::LATENCY},
846 {"write_latency", PerformanceCounterType::WRITE_LATENCY},
847 {"read_latency", PerformanceCounterType::READ_LATENCY},
848 };
849
850 PyObject *py_query = nullptr;
851 if (!PyArg_ParseTuple(args, "O:ceph_add_osd_perf_query", &py_query)) {
852 derr << "Invalid args!" << dendl;
853 return nullptr;
854 }
855 if (!PyDict_Check(py_query)) {
856 derr << __func__ << " arg not a dict" << dendl;
857 Py_RETURN_NONE;
858 }
859
860 PyObject *query_params = PyDict_Items(py_query);
861 OSDPerfMetricQuery query;
862 std::optional<OSDPerfMetricLimit> limit;
863
864 // {
865 // 'key_descriptor': [
866 // {'type': subkey_type, 'regex': regex_pattern},
867 // ...
868 // ],
869 // 'performance_counter_descriptors': [
870 // list, of, descriptor, types
871 // ],
872 // 'limit': {'order_by': performance_counter_type, 'max_count': n},
873 // }
874
875 for (int i = 0; i < PyList_Size(query_params); ++i) {
876 PyObject *kv = PyList_GET_ITEM(query_params, i);
877 char *query_param_name = nullptr;
878 PyObject *query_param_val = nullptr;
879 if (!PyArg_ParseTuple(kv, "sO:pair", &query_param_name, &query_param_val)) {
880 derr << __func__ << " dict item " << i << " not a size 2 tuple" << dendl;
881 Py_RETURN_NONE;
882 }
883 if (query_param_name == NAME_KEY_DESCRIPTOR) {
884 if (!PyList_Check(query_param_val)) {
885 derr << __func__ << " " << query_param_name << " not a list" << dendl;
886 Py_RETURN_NONE;
887 }
888 for (int j = 0; j < PyList_Size(query_param_val); j++) {
889 PyObject *sub_key = PyList_GET_ITEM(query_param_val, j);
890 if (!PyDict_Check(sub_key)) {
891 derr << __func__ << " query " << query_param_name << " item " << j
892 << " not a dict" << dendl;
893 Py_RETURN_NONE;
894 }
895 OSDPerfMetricSubKeyDescriptor d;
896 PyObject *sub_key_params = PyDict_Items(sub_key);
897 for (int k = 0; k < PyList_Size(sub_key_params); ++k) {
898 PyObject *pair = PyList_GET_ITEM(sub_key_params, k);
899 if (!PyTuple_Check(pair)) {
900 derr << __func__ << " query " << query_param_name << " item " << j
901 << " pair " << k << " not a tuple" << dendl;
902 Py_RETURN_NONE;
903 }
904 char *param_name = nullptr;
905 PyObject *param_value = nullptr;
906 if (!PyArg_ParseTuple(pair, "sO:pair", &param_name, &param_value)) {
907 derr << __func__ << " query " << query_param_name << " item " << j
908 << " pair " << k << " not a size 2 tuple" << dendl;
909 Py_RETURN_NONE;
910 }
911 if (param_name == NAME_SUB_KEY_TYPE) {
912 if (!PyUnicode_Check(param_value)) {
913 derr << __func__ << " query " << query_param_name << " item " << j
914 << " contains invalid param " << param_name << dendl;
915 Py_RETURN_NONE;
916 }
917 auto type = PyUnicode_AsUTF8(param_value);
918 auto it = sub_key_types.find(type);
919 if (it == sub_key_types.end()) {
920 derr << __func__ << " query " << query_param_name << " item " << j
921 << " contains invalid type " << dendl;
922 Py_RETURN_NONE;
923 }
924 d.type = it->second;
925 } else if (param_name == NAME_SUB_KEY_REGEX) {
926 if (!PyUnicode_Check(param_value)) {
927 derr << __func__ << " query " << query_param_name << " item " << j
928 << " contains invalid param " << param_name << dendl;
929 Py_RETURN_NONE;
930 }
931 d.regex_str = PyUnicode_AsUTF8(param_value);
932 try {
933 d.regex = d.regex_str.c_str();
934 } catch (const std::regex_error& e) {
935 derr << __func__ << " query " << query_param_name << " item " << j
936 << " contains invalid regex " << d.regex_str << dendl;
937 Py_RETURN_NONE;
938 }
939 if (d.regex.mark_count() == 0) {
940 derr << __func__ << " query " << query_param_name << " item " << j
941 << " regex " << d.regex_str << ": no capturing groups"
942 << dendl;
943 Py_RETURN_NONE;
944 }
945 } else {
946 derr << __func__ << " query " << query_param_name << " item " << j
947 << " contains invalid param " << param_name << dendl;
948 Py_RETURN_NONE;
949 }
950 }
951 if (d.type == static_cast<OSDPerfMetricSubKeyType>(-1) ||
952 d.regex_str.empty()) {
953 derr << __func__ << " query " << query_param_name << " item " << i
954 << " invalid" << dendl;
955 Py_RETURN_NONE;
956 }
957 query.key_descriptor.push_back(d);
958 }
959 } else if (query_param_name == NAME_COUNTERS_DESCRIPTORS) {
960 if (!PyList_Check(query_param_val)) {
961 derr << __func__ << " " << query_param_name << " not a list" << dendl;
962 Py_RETURN_NONE;
963 }
964 for (int j = 0; j < PyList_Size(query_param_val); j++) {
965 PyObject *py_type = PyList_GET_ITEM(query_param_val, j);
966 if (!PyUnicode_Check(py_type)) {
967 derr << __func__ << " query " << query_param_name << " item " << j
968 << " not a string" << dendl;
969 Py_RETURN_NONE;
970 }
971 auto type = PyUnicode_AsUTF8(py_type);
972 auto it = counter_types.find(type);
973 if (it == counter_types.end()) {
974 derr << __func__ << " query " << query_param_name << " item " << type
975 << " is not valid type" << dendl;
976 Py_RETURN_NONE;
977 }
978 query.performance_counter_descriptors.push_back(it->second);
979 }
980 } else if (query_param_name == NAME_LIMIT) {
981 if (!PyDict_Check(query_param_val)) {
982 derr << __func__ << " query " << query_param_name << " not a dict"
983 << dendl;
984 Py_RETURN_NONE;
985 }
986
987 limit = OSDPerfMetricLimit();
988 PyObject *limit_params = PyDict_Items(query_param_val);
989
990 for (int j = 0; j < PyList_Size(limit_params); ++j) {
991 PyObject *kv = PyList_GET_ITEM(limit_params, j);
992 char *limit_param_name = nullptr;
993 PyObject *limit_param_val = nullptr;
994 if (!PyArg_ParseTuple(kv, "sO:pair", &limit_param_name,
995 &limit_param_val)) {
996 derr << __func__ << " limit item " << j << " not a size 2 tuple"
997 << dendl;
998 Py_RETURN_NONE;
999 }
1000
1001 if (limit_param_name == NAME_LIMIT_ORDER_BY) {
1002 if (!PyUnicode_Check(limit_param_val)) {
1003 derr << __func__ << " " << limit_param_name << " not a string"
1004 << dendl;
1005 Py_RETURN_NONE;
1006 }
1007 auto order_by = PyUnicode_AsUTF8(limit_param_val);
1008 auto it = counter_types.find(order_by);
1009 if (it == counter_types.end()) {
1010 derr << __func__ << " limit " << limit_param_name
1011 << " not a valid counter type" << dendl;
1012 Py_RETURN_NONE;
1013 }
1014 limit->order_by = it->second;
1015 } else if (limit_param_name == NAME_LIMIT_MAX_COUNT) {
1016 if (!PyLong_Check(limit_param_val)) {
1017 derr << __func__ << " " << limit_param_name << " not an int"
1018 << dendl;
1019 Py_RETURN_NONE;
1020 }
1021 limit->max_count = PyLong_AsLong(limit_param_val);
1022 } else {
1023 derr << __func__ << " unknown limit param: " << limit_param_name
1024 << dendl;
1025 Py_RETURN_NONE;
1026 }
1027 }
1028 } else {
1029 derr << __func__ << " unknown query param: " << query_param_name << dendl;
1030 Py_RETURN_NONE;
1031 }
1032 }
1033
1034 if (query.key_descriptor.empty() ||
1035 query.performance_counter_descriptors.empty()) {
1036 derr << __func__ << " invalid query" << dendl;
1037 Py_RETURN_NONE;
1038 }
1039
1040 if (limit) {
1041 auto &ds = query.performance_counter_descriptors;
1042 if (std::find(ds.begin(), ds.end(), limit->order_by) == ds.end()) {
1043 derr << __func__ << " limit order_by " << limit->order_by
1044 << " not in performance_counter_descriptors" << dendl;
1045 Py_RETURN_NONE;
1046 }
1047 }
1048
1049 auto query_id = self->py_modules->add_osd_perf_query(query, limit);
1050 return PyLong_FromLong(query_id);
1051 }
1052
1053 static PyObject*
1054 ceph_remove_osd_perf_query(BaseMgrModule *self, PyObject *args)
1055 {
1056 MetricQueryID query_id;
1057 if (!PyArg_ParseTuple(args, "i:ceph_remove_osd_perf_query", &query_id)) {
1058 derr << "Invalid args!" << dendl;
1059 return nullptr;
1060 }
1061
1062 self->py_modules->remove_osd_perf_query(query_id);
1063 Py_RETURN_NONE;
1064 }
1065
1066 static PyObject*
1067 ceph_get_osd_perf_counters(BaseMgrModule *self, PyObject *args)
1068 {
1069 MetricQueryID query_id;
1070 if (!PyArg_ParseTuple(args, "i:ceph_get_osd_perf_counters", &query_id)) {
1071 derr << "Invalid args!" << dendl;
1072 return nullptr;
1073 }
1074
1075 return self->py_modules->get_osd_perf_counters(query_id);
1076 }
1077
1078 // MDS perf query interface -- mostly follows ceph_add_osd_perf_query()
1079 // style
1080
1081 static PyObject*
1082 ceph_add_mds_perf_query(BaseMgrModule *self, PyObject *args)
1083 {
1084 static const std::string NAME_KEY_DESCRIPTOR = "key_descriptor";
1085 static const std::string NAME_COUNTERS_DESCRIPTORS =
1086 "performance_counter_descriptors";
1087 static const std::string NAME_LIMIT = "limit";
1088 static const std::string NAME_SUB_KEY_TYPE = "type";
1089 static const std::string NAME_SUB_KEY_REGEX = "regex";
1090 static const std::string NAME_LIMIT_ORDER_BY = "order_by";
1091 static const std::string NAME_LIMIT_MAX_COUNT = "max_count";
1092 static const std::map<std::string, MDSPerfMetricSubKeyType> sub_key_types = {
1093 {"mds_rank", MDSPerfMetricSubKeyType::MDS_RANK},
1094 {"client_id", MDSPerfMetricSubKeyType::CLIENT_ID},
1095 };
1096 static const std::map<std::string, MDSPerformanceCounterType> counter_types = {
1097 {"cap_hit", MDSPerformanceCounterType::CAP_HIT_METRIC},
1098 {"read_latency", MDSPerformanceCounterType::READ_LATENCY_METRIC},
1099 {"write_latency", MDSPerformanceCounterType::WRITE_LATENCY_METRIC},
1100 {"metadata_latency", MDSPerformanceCounterType::METADATA_LATENCY_METRIC},
1101 {"dentry_lease", MDSPerformanceCounterType::DENTRY_LEASE_METRIC},
1102 {"opened_files", MDSPerformanceCounterType::OPENED_FILES_METRIC},
1103 {"pinned_icaps", MDSPerformanceCounterType::PINNED_ICAPS_METRIC},
1104 {"opened_inodes", MDSPerformanceCounterType::OPENED_INODES_METRIC},
1105 {"read_io_sizes", MDSPerformanceCounterType::READ_IO_SIZES_METRIC},
1106 {"write_io_sizes", MDSPerformanceCounterType::WRITE_IO_SIZES_METRIC},
1107 {"avg_read_latency", MDSPerformanceCounterType::AVG_READ_LATENCY_METRIC},
1108 {"stdev_read_latency", MDSPerformanceCounterType::STDEV_READ_LATENCY_METRIC},
1109 {"avg_write_latency", MDSPerformanceCounterType::AVG_WRITE_LATENCY_METRIC},
1110 {"stdev_write_latency", MDSPerformanceCounterType::STDEV_WRITE_LATENCY_METRIC},
1111 {"avg_metadata_latency", MDSPerformanceCounterType::AVG_METADATA_LATENCY_METRIC},
1112 {"stdev_metadata_latency", MDSPerformanceCounterType::STDEV_METADATA_LATENCY_METRIC},
1113 };
1114
1115 PyObject *py_query = nullptr;
1116 if (!PyArg_ParseTuple(args, "O:ceph_add_mds_perf_query", &py_query)) {
1117 derr << "Invalid args!" << dendl;
1118 return nullptr;
1119 }
1120 if (!PyDict_Check(py_query)) {
1121 derr << __func__ << " arg not a dict" << dendl;
1122 Py_RETURN_NONE;
1123 }
1124
1125 PyObject *query_params = PyDict_Items(py_query);
1126 MDSPerfMetricQuery query;
1127 std::optional<MDSPerfMetricLimit> limit;
1128
1129 // {
1130 // 'key_descriptor': [
1131 // {'type': subkey_type, 'regex': regex_pattern},
1132 // ...
1133 // ],
1134 // 'performance_counter_descriptors': [
1135 // list, of, descriptor, types
1136 // ],
1137 // 'limit': {'order_by': performance_counter_type, 'max_count': n},
1138 // }
1139
1140 for (int i = 0; i < PyList_Size(query_params); ++i) {
1141 PyObject *kv = PyList_GET_ITEM(query_params, i);
1142 char *query_param_name = nullptr;
1143 PyObject *query_param_val = nullptr;
1144 if (!PyArg_ParseTuple(kv, "sO:pair", &query_param_name, &query_param_val)) {
1145 derr << __func__ << " dict item " << i << " not a size 2 tuple" << dendl;
1146 Py_RETURN_NONE;
1147 }
1148 if (query_param_name == NAME_KEY_DESCRIPTOR) {
1149 if (!PyList_Check(query_param_val)) {
1150 derr << __func__ << " " << query_param_name << " not a list" << dendl;
1151 Py_RETURN_NONE;
1152 }
1153 for (int j = 0; j < PyList_Size(query_param_val); j++) {
1154 PyObject *sub_key = PyList_GET_ITEM(query_param_val, j);
1155 if (!PyDict_Check(sub_key)) {
1156 derr << __func__ << " query " << query_param_name << " item " << j
1157 << " not a dict" << dendl;
1158 Py_RETURN_NONE;
1159 }
1160 MDSPerfMetricSubKeyDescriptor d;
1161 PyObject *sub_key_params = PyDict_Items(sub_key);
1162 for (int k = 0; k < PyList_Size(sub_key_params); ++k) {
1163 PyObject *pair = PyList_GET_ITEM(sub_key_params, k);
1164 if (!PyTuple_Check(pair)) {
1165 derr << __func__ << " query " << query_param_name << " item " << j
1166 << " pair " << k << " not a tuple" << dendl;
1167 Py_RETURN_NONE;
1168 }
1169 char *param_name = nullptr;
1170 PyObject *param_value = nullptr;
1171 if (!PyArg_ParseTuple(pair, "sO:pair", &param_name, &param_value)) {
1172 derr << __func__ << " query " << query_param_name << " item " << j
1173 << " pair " << k << " not a size 2 tuple" << dendl;
1174 Py_RETURN_NONE;
1175 }
1176 if (param_name == NAME_SUB_KEY_TYPE) {
1177 if (!PyUnicode_Check(param_value)) {
1178 derr << __func__ << " query " << query_param_name << " item " << j
1179 << " contains invalid param " << param_name << dendl;
1180 Py_RETURN_NONE;
1181 }
1182 auto type = PyUnicode_AsUTF8(param_value);
1183 auto it = sub_key_types.find(type);
1184 if (it == sub_key_types.end()) {
1185 derr << __func__ << " query " << query_param_name << " item " << j
1186 << " contains invalid type " << dendl;
1187 Py_RETURN_NONE;
1188 }
1189 d.type = it->second;
1190 } else if (param_name == NAME_SUB_KEY_REGEX) {
1191 if (!PyUnicode_Check(param_value)) {
1192 derr << __func__ << " query " << query_param_name << " item " << j
1193 << " contains invalid param " << param_name << dendl;
1194 Py_RETURN_NONE;
1195 }
1196 d.regex_str = PyUnicode_AsUTF8(param_value);
1197 try {
1198 d.regex = d.regex_str.c_str();
1199 } catch (const std::regex_error& e) {
1200 derr << __func__ << " query " << query_param_name << " item " << j
1201 << " contains invalid regex " << d.regex_str << dendl;
1202 Py_RETURN_NONE;
1203 }
1204 if (d.regex.mark_count() == 0) {
1205 derr << __func__ << " query " << query_param_name << " item " << j
1206 << " regex " << d.regex_str << ": no capturing groups"
1207 << dendl;
1208 Py_RETURN_NONE;
1209 }
1210 } else {
1211 derr << __func__ << " query " << query_param_name << " item " << j
1212 << " contains invalid param " << param_name << dendl;
1213 Py_RETURN_NONE;
1214 }
1215 }
1216 if (d.type == static_cast<MDSPerfMetricSubKeyType>(-1) ||
1217 d.regex_str.empty()) {
1218 derr << __func__ << " query " << query_param_name << " item " << i
1219 << " invalid" << dendl;
1220 Py_RETURN_NONE;
1221 }
1222 query.key_descriptor.push_back(d);
1223 }
1224 } else if (query_param_name == NAME_COUNTERS_DESCRIPTORS) {
1225 if (!PyList_Check(query_param_val)) {
1226 derr << __func__ << " " << query_param_name << " not a list" << dendl;
1227 Py_RETURN_NONE;
1228 }
1229 for (int j = 0; j < PyList_Size(query_param_val); j++) {
1230 PyObject *py_type = PyList_GET_ITEM(query_param_val, j);
1231 if (!PyUnicode_Check(py_type)) {
1232 derr << __func__ << " query " << query_param_name << " item " << j
1233 << " not a string" << dendl;
1234 Py_RETURN_NONE;
1235 }
1236 auto type = PyUnicode_AsUTF8(py_type);
1237 auto it = counter_types.find(type);
1238 if (it == counter_types.end()) {
1239 derr << __func__ << " query " << query_param_name << " item " << type
1240 << " is not valid type" << dendl;
1241 Py_RETURN_NONE;
1242 }
1243 query.performance_counter_descriptors.push_back(it->second);
1244 }
1245 } else if (query_param_name == NAME_LIMIT) {
1246 if (!PyDict_Check(query_param_val)) {
1247 derr << __func__ << " query " << query_param_name << " not a dict"
1248 << dendl;
1249 Py_RETURN_NONE;
1250 }
1251
1252 limit = MDSPerfMetricLimit();
1253 PyObject *limit_params = PyDict_Items(query_param_val);
1254
1255 for (int j = 0; j < PyList_Size(limit_params); ++j) {
1256 PyObject *kv = PyList_GET_ITEM(limit_params, j);
1257 char *limit_param_name = nullptr;
1258 PyObject *limit_param_val = nullptr;
1259 if (!PyArg_ParseTuple(kv, "sO:pair", &limit_param_name,
1260 &limit_param_val)) {
1261 derr << __func__ << " limit item " << j << " not a size 2 tuple"
1262 << dendl;
1263 Py_RETURN_NONE;
1264 }
1265
1266 if (limit_param_name == NAME_LIMIT_ORDER_BY) {
1267 if (!PyUnicode_Check(limit_param_val)) {
1268 derr << __func__ << " " << limit_param_name << " not a string"
1269 << dendl;
1270 Py_RETURN_NONE;
1271 }
1272 auto order_by = PyUnicode_AsUTF8(limit_param_val);
1273 auto it = counter_types.find(order_by);
1274 if (it == counter_types.end()) {
1275 derr << __func__ << " limit " << limit_param_name
1276 << " not a valid counter type" << dendl;
1277 Py_RETURN_NONE;
1278 }
1279 limit->order_by = it->second;
1280 } else if (limit_param_name == NAME_LIMIT_MAX_COUNT) {
1281 if (!PyLong_Check(limit_param_val)) {
1282 derr << __func__ << " " << limit_param_name << " not an int"
1283 << dendl;
1284 Py_RETURN_NONE;
1285 }
1286 limit->max_count = PyLong_AsLong(limit_param_val);
1287 } else {
1288 derr << __func__ << " unknown limit param: " << limit_param_name
1289 << dendl;
1290 Py_RETURN_NONE;
1291 }
1292 }
1293 } else {
1294 derr << __func__ << " unknown query param: " << query_param_name << dendl;
1295 Py_RETURN_NONE;
1296 }
1297 }
1298
1299 if (query.key_descriptor.empty()) {
1300 derr << __func__ << " invalid query" << dendl;
1301 Py_RETURN_NONE;
1302 }
1303
1304 if (limit) {
1305 auto &ds = query.performance_counter_descriptors;
1306 if (std::find(ds.begin(), ds.end(), limit->order_by) == ds.end()) {
1307 derr << __func__ << " limit order_by " << limit->order_by
1308 << " not in performance_counter_descriptors" << dendl;
1309 Py_RETURN_NONE;
1310 }
1311 }
1312
1313 auto query_id = self->py_modules->add_mds_perf_query(query, limit);
1314 return PyLong_FromLong(query_id);
1315 }
1316
1317 static PyObject*
1318 ceph_remove_mds_perf_query(BaseMgrModule *self, PyObject *args)
1319 {
1320 MetricQueryID query_id;
1321 if (!PyArg_ParseTuple(args, "i:ceph_remove_mds_perf_query", &query_id)) {
1322 derr << "Invalid args!" << dendl;
1323 return nullptr;
1324 }
1325
1326 self->py_modules->remove_mds_perf_query(query_id);
1327 Py_RETURN_NONE;
1328 }
1329
1330 static PyObject*
1331 ceph_reregister_mds_perf_queries(BaseMgrModule *self, PyObject *args)
1332 {
1333 self->py_modules->reregister_mds_perf_queries();
1334 Py_RETURN_NONE;
1335 }
1336
1337 static PyObject*
1338 ceph_get_mds_perf_counters(BaseMgrModule *self, PyObject *args)
1339 {
1340 MetricQueryID query_id;
1341 if (!PyArg_ParseTuple(args, "i:ceph_get_mds_perf_counters", &query_id)) {
1342 derr << "Invalid args!" << dendl;
1343 return nullptr;
1344 }
1345
1346 return self->py_modules->get_mds_perf_counters(query_id);
1347 }
1348
1349 static PyObject*
1350 ceph_is_authorized(BaseMgrModule *self, PyObject *args)
1351 {
1352 PyObject *args_dict = NULL;
1353 if (!PyArg_ParseTuple(args, "O:ceph_is_authorized", &args_dict)) {
1354 return nullptr;
1355 }
1356
1357 if (!PyDict_Check(args_dict)) {
1358 derr << __func__ << " arg not a dict" << dendl;
1359 Py_RETURN_FALSE;
1360 }
1361
1362 std::map<std::string, std::string> arguments;
1363
1364 PyObject *args_list = PyDict_Items(args_dict);
1365 for (int i = 0; i < PyList_Size(args_list); ++i) {
1366 PyObject *kv = PyList_GET_ITEM(args_list, i);
1367
1368 char *arg_key = nullptr;
1369 char *arg_value = nullptr;
1370 if (!PyArg_ParseTuple(kv, "ss:pair", &arg_key, &arg_value)) {
1371 derr << __func__ << " dict item " << i << " not a size 2 tuple" << dendl;
1372 continue;
1373 }
1374
1375 arguments[arg_key] = arg_value;
1376 }
1377
1378 bool r = without_gil([&] {
1379 return self->this_module->is_authorized(arguments);
1380 });
1381
1382 if (r) {
1383 Py_RETURN_TRUE;
1384 }
1385 Py_RETURN_FALSE;
1386 }
1387
1388 static PyObject*
1389 ceph_register_client(BaseMgrModule *self, PyObject *args)
1390 {
1391 char *addrs = nullptr;
1392 if (!PyArg_ParseTuple(args, "s:ceph_register_client", &addrs)) {
1393 return nullptr;
1394 }
1395 without_gil([&] {
1396 self->py_modules->register_client(self->this_module->get_name(), addrs);
1397 });
1398 Py_RETURN_NONE;
1399 }
1400
1401 static PyObject*
1402 ceph_unregister_client(BaseMgrModule *self, PyObject *args)
1403 {
1404 char *addrs = nullptr;
1405 if (!PyArg_ParseTuple(args, "s:ceph_unregister_client", &addrs)) {
1406 return nullptr;
1407 }
1408 without_gil([&] {
1409 self->py_modules->unregister_client(self->this_module->get_name(), addrs);
1410 });
1411 Py_RETURN_NONE;
1412 }
1413
1414 static PyObject*
1415 ceph_get_daemon_health_metrics(BaseMgrModule *self, PyObject *args)
1416 {
1417 return self->py_modules->get_daemon_health_metrics();
1418 }
1419
1420 PyMethodDef BaseMgrModule_methods[] = {
1421 {"_ceph_get", (PyCFunction)ceph_state_get, METH_VARARGS,
1422 "Get a cluster object"},
1423
1424 {"_ceph_get_server", (PyCFunction)ceph_get_server, METH_VARARGS,
1425 "Get a server object"},
1426
1427 {"_ceph_get_metadata", (PyCFunction)get_metadata, METH_VARARGS,
1428 "Get a service's metadata"},
1429
1430 {"_ceph_get_daemon_status", (PyCFunction)get_daemon_status, METH_VARARGS,
1431 "Get a service's status"},
1432
1433 {"_ceph_send_command", (PyCFunction)ceph_send_command, METH_VARARGS,
1434 "Send a mon command"},
1435
1436 {"_ceph_set_health_checks", (PyCFunction)ceph_set_health_checks, METH_VARARGS,
1437 "Set health checks for this module"},
1438
1439 {"_ceph_get_mgr_id", (PyCFunction)ceph_get_mgr_id, METH_NOARGS,
1440 "Get the name of the Mgr daemon where we are running"},
1441
1442 {"_ceph_get_ceph_conf_path", (PyCFunction)ceph_get_ceph_conf_path, METH_NOARGS,
1443 "Get path to ceph.conf"},
1444
1445 {"_ceph_get_option", (PyCFunction)ceph_option_get, METH_VARARGS,
1446 "Get a native configuration option value"},
1447
1448 {"_ceph_get_foreign_option", (PyCFunction)ceph_foreign_option_get, METH_VARARGS,
1449 "Get a native configuration option value for another entity"},
1450
1451 {"_ceph_get_module_option", (PyCFunction)ceph_get_module_option, METH_VARARGS,
1452 "Get a module configuration option value"},
1453
1454 {"_ceph_get_store_prefix", (PyCFunction)ceph_store_get_prefix, METH_VARARGS,
1455 "Get all KV store values with a given prefix"},
1456
1457 {"_ceph_set_module_option", (PyCFunction)ceph_set_module_option, METH_VARARGS,
1458 "Set a module configuration option value"},
1459
1460 {"_ceph_get_store", (PyCFunction)ceph_store_get, METH_VARARGS,
1461 "Get a stored field"},
1462
1463 {"_ceph_set_store", (PyCFunction)ceph_store_set, METH_VARARGS,
1464 "Set a stored field"},
1465
1466 {"_ceph_get_counter", (PyCFunction)get_counter, METH_VARARGS,
1467 "Get a performance counter"},
1468
1469 {"_ceph_get_latest_counter", (PyCFunction)get_latest_counter, METH_VARARGS,
1470 "Get the latest performance counter"},
1471
1472 {"_ceph_get_perf_schema", (PyCFunction)get_perf_schema, METH_VARARGS,
1473 "Get the performance counter schema"},
1474
1475 {"_ceph_get_rocksdb_version", (PyCFunction)ceph_get_rocksdb_version, METH_NOARGS,
1476 "Get the current RocksDB version number"},
1477
1478 {"_ceph_log", (PyCFunction)ceph_log, METH_VARARGS,
1479 "Emit a (local) log message"},
1480
1481 {"_ceph_cluster_log", (PyCFunction)ceph_cluster_log, METH_VARARGS,
1482 "Emit a cluster log message"},
1483
1484 {"_ceph_get_version", (PyCFunction)ceph_get_version, METH_NOARGS,
1485 "Get the ceph version of this process"},
1486
1487 {"_ceph_get_release_name", (PyCFunction)ceph_get_release_name, METH_NOARGS,
1488 "Get the ceph release name of this process"},
1489
1490 {"_ceph_lookup_release_name", (PyCFunction)ceph_lookup_release_name, METH_VARARGS,
1491 "Get the ceph release name for a given major number"},
1492
1493 {"_ceph_get_context", (PyCFunction)ceph_get_context, METH_NOARGS,
1494 "Get a CephContext* in a python capsule"},
1495
1496 {"_ceph_get_osdmap", (PyCFunction)ceph_get_osdmap, METH_NOARGS,
1497 "Get an OSDMap* in a python capsule"},
1498
1499 {"_ceph_set_uri", (PyCFunction)ceph_set_uri, METH_VARARGS,
1500 "Advertize a service URI served by this module"},
1501
1502 {"_ceph_set_device_wear_level", (PyCFunction)ceph_set_wear_level, METH_VARARGS,
1503 "Set device wear_level value"},
1504
1505 {"_ceph_have_mon_connection", (PyCFunction)ceph_have_mon_connection,
1506 METH_NOARGS, "Find out whether this mgr daemon currently has "
1507 "a connection to a monitor"},
1508
1509 {"_ceph_update_progress_event", (PyCFunction)ceph_update_progress_event,
1510 METH_VARARGS, "Update status of a progress event"},
1511 {"_ceph_complete_progress_event", (PyCFunction)ceph_complete_progress_event,
1512 METH_VARARGS, "Complete a progress event"},
1513 {"_ceph_clear_all_progress_events", (PyCFunction)ceph_clear_all_progress_events,
1514 METH_NOARGS, "Clear all progress events"},
1515
1516 {"_ceph_dispatch_remote", (PyCFunction)ceph_dispatch_remote,
1517 METH_VARARGS, "Dispatch a call to another module"},
1518
1519 {"_ceph_add_osd_perf_query", (PyCFunction)ceph_add_osd_perf_query,
1520 METH_VARARGS, "Add an osd perf query"},
1521
1522 {"_ceph_remove_osd_perf_query", (PyCFunction)ceph_remove_osd_perf_query,
1523 METH_VARARGS, "Remove an osd perf query"},
1524
1525 {"_ceph_get_osd_perf_counters", (PyCFunction)ceph_get_osd_perf_counters,
1526 METH_VARARGS, "Get osd perf counters"},
1527
1528 {"_ceph_add_mds_perf_query", (PyCFunction)ceph_add_mds_perf_query,
1529 METH_VARARGS, "Add an mds perf query"},
1530
1531 {"_ceph_remove_mds_perf_query", (PyCFunction)ceph_remove_mds_perf_query,
1532 METH_VARARGS, "Remove an mds perf query"},
1533
1534 {"_ceph_reregister_mds_perf_queries", (PyCFunction)ceph_reregister_mds_perf_queries,
1535 METH_NOARGS, "Re-register mds perf queries"},
1536
1537 {"_ceph_get_mds_perf_counters", (PyCFunction)ceph_get_mds_perf_counters,
1538 METH_VARARGS, "Get mds perf counters"},
1539
1540 {"_ceph_is_authorized", (PyCFunction)ceph_is_authorized,
1541 METH_VARARGS, "Verify the current session caps are valid"},
1542
1543 {"_ceph_register_client", (PyCFunction)ceph_register_client,
1544 METH_VARARGS, "Register RADOS instance for potential blocklisting"},
1545
1546 {"_ceph_unregister_client", (PyCFunction)ceph_unregister_client,
1547 METH_VARARGS, "Unregister RADOS instance for potential blocklisting"},
1548
1549 {"_ceph_get_daemon_health_metrics", (PyCFunction)ceph_get_daemon_health_metrics,
1550 METH_VARARGS, "Get health metrics for all daemons"},
1551
1552 {NULL, NULL, 0, NULL}
1553 };
1554
1555
1556 static PyObject *
1557 BaseMgrModule_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1558 {
1559 BaseMgrModule *self;
1560
1561 self = (BaseMgrModule *)type->tp_alloc(type, 0);
1562
1563 return (PyObject *)self;
1564 }
1565
1566 static int
1567 BaseMgrModule_init(BaseMgrModule *self, PyObject *args, PyObject *kwds)
1568 {
1569 PyObject *py_modules_capsule = nullptr;
1570 PyObject *this_module_capsule = nullptr;
1571 static const char *kwlist[] = {"py_modules", "this_module", NULL};
1572
1573 if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO",
1574 const_cast<char**>(kwlist),
1575 &py_modules_capsule,
1576 &this_module_capsule)) {
1577 return -1;
1578 }
1579
1580 self->py_modules = static_cast<ActivePyModules*>(PyCapsule_GetPointer(
1581 py_modules_capsule, nullptr));
1582 ceph_assert(self->py_modules);
1583 self->this_module = static_cast<ActivePyModule*>(PyCapsule_GetPointer(
1584 this_module_capsule, nullptr));
1585 ceph_assert(self->this_module);
1586
1587 return 0;
1588 }
1589
1590 PyTypeObject BaseMgrModuleType = {
1591 PyVarObject_HEAD_INIT(NULL, 0)
1592 "ceph_module.BaseMgrModule", /* tp_name */
1593 sizeof(BaseMgrModule), /* tp_basicsize */
1594 0, /* tp_itemsize */
1595 0, /* tp_dealloc */
1596 0, /* tp_print */
1597 0, /* tp_getattr */
1598 0, /* tp_setattr */
1599 0, /* tp_compare */
1600 0, /* tp_repr */
1601 0, /* tp_as_number */
1602 0, /* tp_as_sequence */
1603 0, /* tp_as_mapping */
1604 0, /* tp_hash */
1605 0, /* tp_call */
1606 0, /* tp_str */
1607 0, /* tp_getattro */
1608 0, /* tp_setattro */
1609 0, /* tp_as_buffer */
1610 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1611 "ceph-mgr Python Plugin", /* tp_doc */
1612 0, /* tp_traverse */
1613 0, /* tp_clear */
1614 0, /* tp_richcompare */
1615 0, /* tp_weaklistoffset */
1616 0, /* tp_iter */
1617 0, /* tp_iternext */
1618 BaseMgrModule_methods, /* tp_methods */
1619 0, /* tp_members */
1620 0, /* tp_getset */
1621 0, /* tp_base */
1622 0, /* tp_dict */
1623 0, /* tp_descr_get */
1624 0, /* tp_descr_set */
1625 0, /* tp_dictoffset */
1626 (initproc)BaseMgrModule_init, /* tp_init */
1627 0, /* tp_alloc */
1628 BaseMgrModule_new, /* tp_new */
1629 };