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