]> git.proxmox.com Git - ceph.git/blob - ceph/src/mgr/BaseMgrModule.cc
import ceph 15.2.10
[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 new LambdaContext([command_c, command_r](int wait_r){
167 command_c->complete(command_r);
168 })
169 );
170 });
171
172 self->py_modules->get_monc().start_mon_command(
173 name,
174 {cmd_json},
175 inbuf,
176 &command_c->outbl,
177 &command_c->outs,
178 new C_OnFinisher(c, &self->py_modules->cmd_finisher));
179 } else if (std::string(type) == "osd") {
180 std::string err;
181 uint64_t osd_id = strict_strtoll(name, 10, &err);
182 if (!err.empty()) {
183 delete command_c;
184 string msg("invalid osd_id: ");
185 msg.append("\"").append(name).append("\"");
186 PyEval_RestoreThread(tstate);
187 PyErr_SetString(PyExc_ValueError, msg.c_str());
188 return nullptr;
189 }
190
191 ceph_tid_t tid;
192 self->py_modules->get_objecter().osd_command(
193 osd_id,
194 {cmd_json},
195 inbuf,
196 &tid,
197 &command_c->outbl,
198 &command_c->outs,
199 new C_OnFinisher(command_c, &self->py_modules->cmd_finisher));
200 } else if (std::string(type) == "mds") {
201 int r = self->py_modules->get_client().mds_command(
202 name,
203 {cmd_json},
204 inbuf,
205 &command_c->outbl,
206 &command_c->outs,
207 new C_OnFinisher(command_c, &self->py_modules->cmd_finisher));
208 if (r != 0) {
209 string msg("failed to send command to mds: ");
210 msg.append(cpp_strerror(r));
211 PyEval_RestoreThread(tstate);
212 PyErr_SetString(PyExc_RuntimeError, msg.c_str());
213 return nullptr;
214 }
215 } else if (std::string(type) == "pg") {
216 pg_t pgid;
217 if (!pgid.parse(name)) {
218 delete command_c;
219 string msg("invalid pgid: ");
220 msg.append("\"").append(name).append("\"");
221 PyEval_RestoreThread(tstate);
222 PyErr_SetString(PyExc_ValueError, msg.c_str());
223 return nullptr;
224 }
225
226 ceph_tid_t tid;
227 self->py_modules->get_objecter().pg_command(
228 pgid,
229 {cmd_json},
230 inbuf,
231 &tid,
232 &command_c->outbl,
233 &command_c->outs,
234 new C_OnFinisher(command_c, &self->py_modules->cmd_finisher));
235 PyEval_RestoreThread(tstate);
236 return nullptr;
237 } else {
238 delete command_c;
239 string msg("unknown service type: ");
240 msg.append(type);
241 PyEval_RestoreThread(tstate);
242 PyErr_SetString(PyExc_ValueError, msg.c_str());
243 return nullptr;
244 }
245
246 PyEval_RestoreThread(tstate);
247 Py_RETURN_NONE;
248 }
249
250 static PyObject*
251 ceph_set_health_checks(BaseMgrModule *self, PyObject *args)
252 {
253 PyObject *checks = NULL;
254 if (!PyArg_ParseTuple(args, "O:ceph_set_health_checks", &checks)) {
255 return NULL;
256 }
257 if (!PyDict_Check(checks)) {
258 derr << __func__ << " arg not a dict" << dendl;
259 Py_RETURN_NONE;
260 }
261 PyObject *checksls = PyDict_Items(checks);
262 health_check_map_t out_checks;
263 for (int i = 0; i < PyList_Size(checksls); ++i) {
264 PyObject *kv = PyList_GET_ITEM(checksls, i);
265 char *check_name = nullptr;
266 PyObject *check_info = nullptr;
267 if (!PyArg_ParseTuple(kv, "sO:pair", &check_name, &check_info)) {
268 derr << __func__ << " dict item " << i
269 << " not a size 2 tuple" << dendl;
270 continue;
271 }
272 if (!PyDict_Check(check_info)) {
273 derr << __func__ << " item " << i << " " << check_name
274 << " value not a dict" << dendl;
275 continue;
276 }
277 health_status_t severity = HEALTH_OK;
278 string summary;
279 list<string> detail;
280 int64_t count = 0;
281 PyObject *infols = PyDict_Items(check_info);
282 for (int j = 0; j < PyList_Size(infols); ++j) {
283 PyObject *pair = PyList_GET_ITEM(infols, j);
284 if (!PyTuple_Check(pair)) {
285 derr << __func__ << " item " << i << " pair " << j
286 << " not a tuple" << dendl;
287 continue;
288 }
289 char *k = nullptr;
290 PyObject *v = nullptr;
291 if (!PyArg_ParseTuple(pair, "sO:pair", &k, &v)) {
292 derr << __func__ << " item " << i << " pair " << j
293 << " not a size 2 tuple" << dendl;
294 continue;
295 }
296 string ks(k);
297 if (ks == "severity") {
298 if (!PyUnicode_Check(v)) {
299 derr << __func__ << " check " << check_name
300 << " severity value not string" << dendl;
301 continue;
302 }
303 if (const string vs = PyUnicode_AsUTF8(v); vs == "warning") {
304 severity = HEALTH_WARN;
305 } else if (vs == "error") {
306 severity = HEALTH_ERR;
307 }
308 } else if (ks == "summary") {
309 if (!PyUnicode_Check(v)) {
310 derr << __func__ << " check " << check_name
311 << " summary value not [unicode] string" << dendl;
312 continue;
313 } else {
314 summary = PyUnicode_AsUTF8(v);
315 }
316 } else if (ks == "count") {
317 if (PyLong_Check(v)) {
318 count = PyLong_AsLong(v);
319 } else {
320 derr << __func__ << " check " << check_name
321 << " count value not int" << dendl;
322 continue;
323 }
324 } else if (ks == "detail") {
325 if (!PyList_Check(v)) {
326 derr << __func__ << " check " << check_name
327 << " detail value not list" << dendl;
328 continue;
329 }
330 for (int k = 0; k < PyList_Size(v); ++k) {
331 PyObject *di = PyList_GET_ITEM(v, k);
332 if (!PyUnicode_Check(di)) {
333 derr << __func__ << " check " << check_name
334 << " detail item " << k << " not a [unicode] string" << dendl;
335 continue;
336 } else {
337 detail.push_back(PyUnicode_AsUTF8(di));
338 }
339 }
340 } else {
341 derr << __func__ << " check " << check_name
342 << " unexpected key " << k << dendl;
343 }
344 }
345 auto& d = out_checks.add(check_name, severity, summary, count);
346 d.detail.swap(detail);
347 }
348
349 JSONFormatter jf(true);
350 dout(10) << "module " << self->this_module->get_name()
351 << " health checks:\n";
352 out_checks.dump(&jf);
353 jf.flush(*_dout);
354 *_dout << dendl;
355
356 PyThreadState *tstate = PyEval_SaveThread();
357 self->py_modules->set_health_checks(self->this_module->get_name(),
358 std::move(out_checks));
359 PyEval_RestoreThread(tstate);
360
361 Py_RETURN_NONE;
362 }
363
364
365 static PyObject*
366 ceph_state_get(BaseMgrModule *self, PyObject *args)
367 {
368 char *what = NULL;
369 if (!PyArg_ParseTuple(args, "s:ceph_state_get", &what)) {
370 return NULL;
371 }
372
373 return self->py_modules->get_python(what);
374 }
375
376
377 static PyObject*
378 ceph_get_server(BaseMgrModule *self, PyObject *args)
379 {
380 char *hostname = NULL;
381 if (!PyArg_ParseTuple(args, "z:ceph_get_server", &hostname)) {
382 return NULL;
383 }
384
385 if (hostname) {
386 return self->py_modules->get_server_python(hostname);
387 } else {
388 return self->py_modules->list_servers_python();
389 }
390 }
391
392 static PyObject*
393 ceph_get_mgr_id(BaseMgrModule *self, PyObject *args)
394 {
395 return PyUnicode_FromString(g_conf()->name.get_id().c_str());
396 }
397
398 static PyObject*
399 ceph_option_get(BaseMgrModule *self, PyObject *args)
400 {
401 char *what = nullptr;
402 if (!PyArg_ParseTuple(args, "s:ceph_option_get", &what)) {
403 derr << "Invalid args!" << dendl;
404 return nullptr;
405 }
406
407 const Option *opt = g_conf().find_option(string(what));
408 if (opt) {
409 std::string value;
410 switch (int r = g_conf().get_val(string(what), &value); r) {
411 case -ENOMEM:
412 PyErr_NoMemory();
413 return nullptr;
414 case -ENAMETOOLONG:
415 PyErr_SetString(PyExc_ValueError, "value too long");
416 return nullptr;
417 default:
418 ceph_assert(r == 0);
419 break;
420 }
421 dout(10) << "ceph_option_get " << what << " found: " << value << dendl;
422 return get_python_typed_option_value(opt->type, value);
423 } else {
424 dout(4) << "ceph_option_get " << what << " not found " << dendl;
425 PyErr_Format(PyExc_KeyError, "option not found: %s", what);
426 return nullptr;
427 }
428 }
429
430 static PyObject*
431 ceph_get_module_option(BaseMgrModule *self, PyObject *args)
432 {
433 char *module = nullptr;
434 char *key = nullptr;
435 char *prefix = nullptr;
436 if (!PyArg_ParseTuple(args, "ss|s:ceph_get_module_option", &module, &key,
437 &prefix)) {
438 derr << "Invalid args!" << dendl;
439 return nullptr;
440 }
441 std::string str_prefix;
442 if (prefix) {
443 str_prefix = prefix;
444 }
445 assert(self->this_module->py_module);
446 auto pResult = self->py_modules->get_typed_config(module, key, str_prefix);
447 return pResult;
448 }
449
450 static PyObject*
451 ceph_store_get_prefix(BaseMgrModule *self, PyObject *args)
452 {
453 char *prefix = nullptr;
454 if (!PyArg_ParseTuple(args, "s:ceph_store_get_prefix", &prefix)) {
455 derr << "Invalid args!" << dendl;
456 return nullptr;
457 }
458
459 return self->py_modules->get_store_prefix(self->this_module->get_name(),
460 prefix);
461 }
462
463 static PyObject*
464 ceph_set_module_option(BaseMgrModule *self, PyObject *args)
465 {
466 char *module = nullptr;
467 char *key = nullptr;
468 char *value = nullptr;
469 if (!PyArg_ParseTuple(args, "ssz:ceph_set_module_option",
470 &module, &key, &value)) {
471 derr << "Invalid args!" << dendl;
472 return nullptr;
473 }
474 boost::optional<string> val;
475 if (value) {
476 val = value;
477 }
478 PyThreadState *tstate = PyEval_SaveThread();
479 self->py_modules->set_config(module, key, val);
480 PyEval_RestoreThread(tstate);
481
482 Py_RETURN_NONE;
483 }
484
485 static PyObject*
486 ceph_store_get(BaseMgrModule *self, PyObject *args)
487 {
488 char *what = nullptr;
489 if (!PyArg_ParseTuple(args, "s:ceph_store_get", &what)) {
490 derr << "Invalid args!" << dendl;
491 return nullptr;
492 }
493
494 std::string value;
495 bool found = self->py_modules->get_store(self->this_module->get_name(),
496 what, &value);
497 if (found) {
498 dout(10) << "ceph_store_get " << what << " found: " << value.c_str() << dendl;
499 return PyUnicode_FromString(value.c_str());
500 } else {
501 dout(4) << "ceph_store_get " << what << " not found " << dendl;
502 Py_RETURN_NONE;
503 }
504 }
505
506 static PyObject*
507 ceph_store_set(BaseMgrModule *self, PyObject *args)
508 {
509 char *key = nullptr;
510 char *value = nullptr;
511 if (!PyArg_ParseTuple(args, "sz:ceph_store_set", &key, &value)) {
512 return nullptr;
513 }
514 boost::optional<string> val;
515 if (value) {
516 val = value;
517 }
518 PyThreadState *tstate = PyEval_SaveThread();
519 self->py_modules->set_store(self->this_module->get_name(), key, val);
520 PyEval_RestoreThread(tstate);
521
522 Py_RETURN_NONE;
523 }
524
525 static PyObject*
526 get_metadata(BaseMgrModule *self, PyObject *args)
527 {
528 char *svc_name = NULL;
529 char *svc_id = NULL;
530 if (!PyArg_ParseTuple(args, "ss:get_metadata", &svc_name, &svc_id)) {
531 return nullptr;
532 }
533 return self->py_modules->get_metadata_python(svc_name, svc_id);
534 }
535
536 static PyObject*
537 get_daemon_status(BaseMgrModule *self, PyObject *args)
538 {
539 char *svc_name = NULL;
540 char *svc_id = NULL;
541 if (!PyArg_ParseTuple(args, "ss:get_daemon_status", &svc_name,
542 &svc_id)) {
543 return nullptr;
544 }
545 return self->py_modules->get_daemon_status_python(svc_name, svc_id);
546 }
547
548 static PyObject*
549 ceph_log(BaseMgrModule *self, PyObject *args)
550 {
551 char *record = nullptr;
552 if (!PyArg_ParseTuple(args, "s:log", &record)) {
553 return nullptr;
554 }
555
556 ceph_assert(self->this_module);
557
558 self->this_module->log(record);
559
560 Py_RETURN_NONE;
561 }
562
563 static PyObject*
564 ceph_cluster_log(BaseMgrModule *self, PyObject *args)
565 {
566 int prio = 0;
567 char *channel = nullptr;
568 char *message = nullptr;
569
570 if (!PyArg_ParseTuple(args, "sis:ceph_cluster_log", &channel, &prio, &message)) {
571 return nullptr;
572 }
573
574 PyThreadState *tstate = PyEval_SaveThread();
575 self->py_modules->cluster_log(channel, (clog_type)prio, message);
576 PyEval_RestoreThread(tstate);
577
578 Py_RETURN_NONE;
579 }
580
581 static PyObject *
582 ceph_get_version(BaseMgrModule *self, PyObject *args)
583 {
584 return PyUnicode_FromString(pretty_version_to_str().c_str());
585 }
586
587 static PyObject *
588 ceph_get_release_name(BaseMgrModule *self, PyObject *args)
589 {
590 return PyUnicode_FromString(ceph_release_to_str());
591 }
592
593 static PyObject *
594 ceph_get_context(BaseMgrModule *self)
595 {
596 return self->py_modules->get_context();
597 }
598
599 static PyObject*
600 get_counter(BaseMgrModule *self, PyObject *args)
601 {
602 char *svc_name = nullptr;
603 char *svc_id = nullptr;
604 char *counter_path = nullptr;
605 if (!PyArg_ParseTuple(args, "sss:get_counter", &svc_name,
606 &svc_id, &counter_path)) {
607 return nullptr;
608 }
609 return self->py_modules->get_counter_python(
610 svc_name, svc_id, counter_path);
611 }
612
613 static PyObject*
614 get_latest_counter(BaseMgrModule *self, PyObject *args)
615 {
616 char *svc_name = nullptr;
617 char *svc_id = nullptr;
618 char *counter_path = nullptr;
619 if (!PyArg_ParseTuple(args, "sss:get_counter", &svc_name,
620 &svc_id, &counter_path)) {
621 return nullptr;
622 }
623 return self->py_modules->get_latest_counter_python(
624 svc_name, svc_id, counter_path);
625 }
626
627 static PyObject*
628 get_perf_schema(BaseMgrModule *self, PyObject *args)
629 {
630 char *type_str = nullptr;
631 char *svc_id = nullptr;
632 if (!PyArg_ParseTuple(args, "ss:get_perf_schema", &type_str,
633 &svc_id)) {
634 return nullptr;
635 }
636
637 return self->py_modules->get_perf_schema_python(type_str, svc_id);
638 }
639
640 static PyObject *
641 ceph_get_osdmap(BaseMgrModule *self, PyObject *args)
642 {
643 return self->py_modules->get_osdmap();
644 }
645
646 static PyObject*
647 ceph_set_uri(BaseMgrModule *self, PyObject *args)
648 {
649 char *svc_str = nullptr;
650 if (!PyArg_ParseTuple(args, "s:ceph_advertize_service",
651 &svc_str)) {
652 return nullptr;
653 }
654
655 // We call down into PyModules even though we have a MgrPyModule
656 // reference here, because MgrPyModule's fields are protected
657 // by PyModules' lock.
658 PyThreadState *tstate = PyEval_SaveThread();
659 self->py_modules->set_uri(self->this_module->get_name(), svc_str);
660 PyEval_RestoreThread(tstate);
661
662 Py_RETURN_NONE;
663 }
664
665 static PyObject*
666 ceph_have_mon_connection(BaseMgrModule *self, PyObject *args)
667 {
668 if (self->py_modules->get_monc().is_connected()) {
669 Py_RETURN_TRUE;
670 } else {
671 Py_RETURN_FALSE;
672 }
673 }
674
675 static PyObject*
676 ceph_update_progress_event(BaseMgrModule *self, PyObject *args)
677 {
678 char *evid = nullptr;
679 char *desc = nullptr;
680 float progress = 0.0;
681 if (!PyArg_ParseTuple(args, "ssf:ceph_update_progress_event",
682 &evid, &desc, &progress)) {
683 return nullptr;
684 }
685
686 PyThreadState *tstate = PyEval_SaveThread();
687 self->py_modules->update_progress_event(evid, desc, progress);
688 PyEval_RestoreThread(tstate);
689
690 Py_RETURN_NONE;
691 }
692
693 static PyObject*
694 ceph_complete_progress_event(BaseMgrModule *self, PyObject *args)
695 {
696 char *evid = nullptr;
697 if (!PyArg_ParseTuple(args, "s:ceph_complete_progress_event",
698 &evid)) {
699 return nullptr;
700 }
701
702 PyThreadState *tstate = PyEval_SaveThread();
703 self->py_modules->complete_progress_event(evid);
704 PyEval_RestoreThread(tstate);
705
706 Py_RETURN_NONE;
707 }
708
709 static PyObject*
710 ceph_clear_all_progress_events(BaseMgrModule *self, PyObject *args)
711 {
712 PyThreadState *tstate = PyEval_SaveThread();
713 self->py_modules->clear_all_progress_events();
714 PyEval_RestoreThread(tstate);
715
716 Py_RETURN_NONE;
717 }
718
719
720
721 static PyObject *
722 ceph_dispatch_remote(BaseMgrModule *self, PyObject *args)
723 {
724 char *other_module = nullptr;
725 char *method = nullptr;
726 PyObject *remote_args = nullptr;
727 PyObject *remote_kwargs = nullptr;
728 if (!PyArg_ParseTuple(args, "ssOO:ceph_dispatch_remote",
729 &other_module, &method, &remote_args, &remote_kwargs)) {
730 return nullptr;
731 }
732
733 // Early error handling, because if the module doesn't exist then we
734 // won't be able to use its thread state to set python error state
735 // inside dispatch_remote().
736 if (!self->py_modules->module_exists(other_module)) {
737 derr << "no module '" << other_module << "'" << dendl;
738 PyErr_SetString(PyExc_ImportError, "Module not found");
739 return nullptr;
740 }
741
742 // Drop GIL from calling python thread state, it will be taken
743 // both for checking for method existence and for executing method.
744 PyThreadState *tstate = PyEval_SaveThread();
745
746 if (!self->py_modules->method_exists(other_module, method)) {
747 PyEval_RestoreThread(tstate);
748 PyErr_SetString(PyExc_NameError, "Method not found");
749 return nullptr;
750 }
751
752 std::string err;
753 auto result = self->py_modules->dispatch_remote(other_module, method,
754 remote_args, remote_kwargs, &err);
755
756 PyEval_RestoreThread(tstate);
757
758 if (result == nullptr) {
759 std::stringstream ss;
760 ss << "Remote method threw exception: " << err;
761 PyErr_SetString(PyExc_RuntimeError, ss.str().c_str());
762 derr << ss.str() << dendl;
763 }
764
765 return result;
766 }
767
768 static PyObject*
769 ceph_add_osd_perf_query(BaseMgrModule *self, PyObject *args)
770 {
771 static const std::string NAME_KEY_DESCRIPTOR = "key_descriptor";
772 static const std::string NAME_COUNTERS_DESCRIPTORS =
773 "performance_counter_descriptors";
774 static const std::string NAME_LIMIT = "limit";
775 static const std::string NAME_SUB_KEY_TYPE = "type";
776 static const std::string NAME_SUB_KEY_REGEX = "regex";
777 static const std::string NAME_LIMIT_ORDER_BY = "order_by";
778 static const std::string NAME_LIMIT_MAX_COUNT = "max_count";
779 static const std::map<std::string, OSDPerfMetricSubKeyType> sub_key_types = {
780 {"client_id", OSDPerfMetricSubKeyType::CLIENT_ID},
781 {"client_address", OSDPerfMetricSubKeyType::CLIENT_ADDRESS},
782 {"pool_id", OSDPerfMetricSubKeyType::POOL_ID},
783 {"namespace", OSDPerfMetricSubKeyType::NAMESPACE},
784 {"osd_id", OSDPerfMetricSubKeyType::OSD_ID},
785 {"pg_id", OSDPerfMetricSubKeyType::PG_ID},
786 {"object_name", OSDPerfMetricSubKeyType::OBJECT_NAME},
787 {"snap_id", OSDPerfMetricSubKeyType::SNAP_ID},
788 };
789 static const std::map<std::string, PerformanceCounterType> counter_types = {
790 {"ops", PerformanceCounterType::OPS},
791 {"write_ops", PerformanceCounterType::WRITE_OPS},
792 {"read_ops", PerformanceCounterType::READ_OPS},
793 {"bytes", PerformanceCounterType::BYTES},
794 {"write_bytes", PerformanceCounterType::WRITE_BYTES},
795 {"read_bytes", PerformanceCounterType::READ_BYTES},
796 {"latency", PerformanceCounterType::LATENCY},
797 {"write_latency", PerformanceCounterType::WRITE_LATENCY},
798 {"read_latency", PerformanceCounterType::READ_LATENCY},
799 };
800
801 PyObject *py_query = nullptr;
802 if (!PyArg_ParseTuple(args, "O:ceph_add_osd_perf_query", &py_query)) {
803 derr << "Invalid args!" << dendl;
804 return nullptr;
805 }
806 if (!PyDict_Check(py_query)) {
807 derr << __func__ << " arg not a dict" << dendl;
808 Py_RETURN_NONE;
809 }
810
811 PyObject *query_params = PyDict_Items(py_query);
812 OSDPerfMetricQuery query;
813 std::optional<OSDPerfMetricLimit> limit;
814
815 // {
816 // 'key_descriptor': [
817 // {'type': subkey_type, 'regex': regex_pattern},
818 // ...
819 // ],
820 // 'performance_counter_descriptors': [
821 // list, of, descriptor, types
822 // ],
823 // 'limit': {'order_by': performance_counter_type, 'max_count': n},
824 // }
825
826 for (int i = 0; i < PyList_Size(query_params); ++i) {
827 PyObject *kv = PyList_GET_ITEM(query_params, i);
828 char *query_param_name = nullptr;
829 PyObject *query_param_val = nullptr;
830 if (!PyArg_ParseTuple(kv, "sO:pair", &query_param_name, &query_param_val)) {
831 derr << __func__ << " dict item " << i << " not a size 2 tuple" << dendl;
832 Py_RETURN_NONE;
833 }
834 if (query_param_name == NAME_KEY_DESCRIPTOR) {
835 if (!PyList_Check(query_param_val)) {
836 derr << __func__ << " " << query_param_name << " not a list" << dendl;
837 Py_RETURN_NONE;
838 }
839 for (int j = 0; j < PyList_Size(query_param_val); j++) {
840 PyObject *sub_key = PyList_GET_ITEM(query_param_val, j);
841 if (!PyDict_Check(sub_key)) {
842 derr << __func__ << " query " << query_param_name << " item " << j
843 << " not a dict" << dendl;
844 Py_RETURN_NONE;
845 }
846 OSDPerfMetricSubKeyDescriptor d;
847 PyObject *sub_key_params = PyDict_Items(sub_key);
848 for (int k = 0; k < PyList_Size(sub_key_params); ++k) {
849 PyObject *pair = PyList_GET_ITEM(sub_key_params, k);
850 if (!PyTuple_Check(pair)) {
851 derr << __func__ << " query " << query_param_name << " item " << j
852 << " pair " << k << " not a tuple" << dendl;
853 Py_RETURN_NONE;
854 }
855 char *param_name = nullptr;
856 PyObject *param_value = nullptr;
857 if (!PyArg_ParseTuple(pair, "sO:pair", &param_name, &param_value)) {
858 derr << __func__ << " query " << query_param_name << " item " << j
859 << " pair " << k << " not a size 2 tuple" << dendl;
860 Py_RETURN_NONE;
861 }
862 if (param_name == NAME_SUB_KEY_TYPE) {
863 if (!PyUnicode_Check(param_value)) {
864 derr << __func__ << " query " << query_param_name << " item " << j
865 << " contains invalid param " << param_name << dendl;
866 Py_RETURN_NONE;
867 }
868 auto type = PyUnicode_AsUTF8(param_value);
869 auto it = sub_key_types.find(type);
870 if (it == sub_key_types.end()) {
871 derr << __func__ << " query " << query_param_name << " item " << j
872 << " contains invalid type " << dendl;
873 Py_RETURN_NONE;
874 }
875 d.type = it->second;
876 } else if (param_name == NAME_SUB_KEY_REGEX) {
877 if (!PyUnicode_Check(param_value)) {
878 derr << __func__ << " query " << query_param_name << " item " << j
879 << " contains invalid param " << param_name << dendl;
880 Py_RETURN_NONE;
881 }
882 d.regex_str = PyUnicode_AsUTF8(param_value);
883 try {
884 d.regex = d.regex_str.c_str();
885 } catch (const std::regex_error& e) {
886 derr << __func__ << " query " << query_param_name << " item " << j
887 << " contains invalid regex " << d.regex_str << dendl;
888 Py_RETURN_NONE;
889 }
890 if (d.regex.mark_count() == 0) {
891 derr << __func__ << " query " << query_param_name << " item " << j
892 << " regex " << d.regex_str << ": no capturing groups"
893 << dendl;
894 Py_RETURN_NONE;
895 }
896 } else {
897 derr << __func__ << " query " << query_param_name << " item " << j
898 << " contains invalid param " << param_name << dendl;
899 Py_RETURN_NONE;
900 }
901 }
902 if (d.type == static_cast<OSDPerfMetricSubKeyType>(-1) ||
903 d.regex_str.empty()) {
904 derr << __func__ << " query " << query_param_name << " item " << i
905 << " invalid" << dendl;
906 Py_RETURN_NONE;
907 }
908 query.key_descriptor.push_back(d);
909 }
910 } else if (query_param_name == NAME_COUNTERS_DESCRIPTORS) {
911 if (!PyList_Check(query_param_val)) {
912 derr << __func__ << " " << query_param_name << " not a list" << dendl;
913 Py_RETURN_NONE;
914 }
915 for (int j = 0; j < PyList_Size(query_param_val); j++) {
916 PyObject *py_type = PyList_GET_ITEM(query_param_val, j);
917 if (!PyUnicode_Check(py_type)) {
918 derr << __func__ << " query " << query_param_name << " item " << j
919 << " not a string" << dendl;
920 Py_RETURN_NONE;
921 }
922 auto type = PyUnicode_AsUTF8(py_type);
923 auto it = counter_types.find(type);
924 if (it == counter_types.end()) {
925 derr << __func__ << " query " << query_param_name << " item " << type
926 << " is not valid type" << dendl;
927 Py_RETURN_NONE;
928 }
929 query.performance_counter_descriptors.push_back(it->second);
930 }
931 } else if (query_param_name == NAME_LIMIT) {
932 if (!PyDict_Check(query_param_val)) {
933 derr << __func__ << " query " << query_param_name << " not a dict"
934 << dendl;
935 Py_RETURN_NONE;
936 }
937
938 limit = OSDPerfMetricLimit();
939 PyObject *limit_params = PyDict_Items(query_param_val);
940
941 for (int j = 0; j < PyList_Size(limit_params); ++j) {
942 PyObject *kv = PyList_GET_ITEM(limit_params, j);
943 char *limit_param_name = nullptr;
944 PyObject *limit_param_val = nullptr;
945 if (!PyArg_ParseTuple(kv, "sO:pair", &limit_param_name,
946 &limit_param_val)) {
947 derr << __func__ << " limit item " << j << " not a size 2 tuple"
948 << dendl;
949 Py_RETURN_NONE;
950 }
951
952 if (limit_param_name == NAME_LIMIT_ORDER_BY) {
953 if (!PyUnicode_Check(limit_param_val)) {
954 derr << __func__ << " " << limit_param_name << " not a string"
955 << dendl;
956 Py_RETURN_NONE;
957 }
958 auto order_by = PyUnicode_AsUTF8(limit_param_val);
959 auto it = counter_types.find(order_by);
960 if (it == counter_types.end()) {
961 derr << __func__ << " limit " << limit_param_name
962 << " not a valid counter type" << dendl;
963 Py_RETURN_NONE;
964 }
965 limit->order_by = it->second;
966 } else if (limit_param_name == NAME_LIMIT_MAX_COUNT) {
967 if (!PyLong_Check(limit_param_val)) {
968 derr << __func__ << " " << limit_param_name << " not an int"
969 << dendl;
970 Py_RETURN_NONE;
971 }
972 limit->max_count = PyLong_AsLong(limit_param_val);
973 } else {
974 derr << __func__ << " unknown limit param: " << limit_param_name
975 << dendl;
976 Py_RETURN_NONE;
977 }
978 }
979 } else {
980 derr << __func__ << " unknown query param: " << query_param_name << dendl;
981 Py_RETURN_NONE;
982 }
983 }
984
985 if (query.key_descriptor.empty() ||
986 query.performance_counter_descriptors.empty()) {
987 derr << __func__ << " invalid query" << dendl;
988 Py_RETURN_NONE;
989 }
990
991 if (limit) {
992 auto &ds = query.performance_counter_descriptors;
993 if (std::find(ds.begin(), ds.end(), limit->order_by) == ds.end()) {
994 derr << __func__ << " limit order_by " << limit->order_by
995 << " not in performance_counter_descriptors" << dendl;
996 Py_RETURN_NONE;
997 }
998 }
999
1000 auto query_id = self->py_modules->add_osd_perf_query(query, limit);
1001 return PyLong_FromLong(query_id);
1002 }
1003
1004 static PyObject*
1005 ceph_remove_osd_perf_query(BaseMgrModule *self, PyObject *args)
1006 {
1007 MetricQueryID query_id;
1008 if (!PyArg_ParseTuple(args, "i:ceph_remove_osd_perf_query", &query_id)) {
1009 derr << "Invalid args!" << dendl;
1010 return nullptr;
1011 }
1012
1013 self->py_modules->remove_osd_perf_query(query_id);
1014 Py_RETURN_NONE;
1015 }
1016
1017 static PyObject*
1018 ceph_get_osd_perf_counters(BaseMgrModule *self, PyObject *args)
1019 {
1020 MetricQueryID query_id;
1021 if (!PyArg_ParseTuple(args, "i:ceph_get_osd_perf_counters", &query_id)) {
1022 derr << "Invalid args!" << dendl;
1023 return nullptr;
1024 }
1025
1026 return self->py_modules->get_osd_perf_counters(query_id);
1027 }
1028
1029 static PyObject*
1030 ceph_is_authorized(BaseMgrModule *self, PyObject *args)
1031 {
1032 PyObject *args_dict = NULL;
1033 if (!PyArg_ParseTuple(args, "O:ceph_is_authorized", &args_dict)) {
1034 return nullptr;
1035 }
1036
1037 if (!PyDict_Check(args_dict)) {
1038 derr << __func__ << " arg not a dict" << dendl;
1039 Py_RETURN_FALSE;
1040 }
1041
1042 std::map<std::string, std::string> arguments;
1043
1044 PyObject *args_list = PyDict_Items(args_dict);
1045 for (int i = 0; i < PyList_Size(args_list); ++i) {
1046 PyObject *kv = PyList_GET_ITEM(args_list, i);
1047
1048 char *arg_key = nullptr;
1049 char *arg_value = nullptr;
1050 if (!PyArg_ParseTuple(kv, "ss:pair", &arg_key, &arg_value)) {
1051 derr << __func__ << " dict item " << i << " not a size 2 tuple" << dendl;
1052 continue;
1053 }
1054
1055 arguments[arg_key] = arg_value;
1056 }
1057
1058 PyThreadState *tstate = PyEval_SaveThread();
1059 bool r = self->this_module->is_authorized(arguments);
1060 PyEval_RestoreThread(tstate);
1061
1062 if (r) {
1063 Py_RETURN_TRUE;
1064 }
1065 Py_RETURN_FALSE;
1066 }
1067
1068 static PyObject*
1069 ceph_register_client(BaseMgrModule *self, PyObject *args)
1070 {
1071 char *addrs = nullptr;
1072 if (!PyArg_ParseTuple(args, "s:ceph_register_client", &addrs)) {
1073 return nullptr;
1074 }
1075 PyThreadState *tstate = PyEval_SaveThread();
1076 self->py_modules->register_client(self->this_module->get_name(), addrs);
1077 PyEval_RestoreThread(tstate);
1078 Py_RETURN_NONE;
1079 }
1080
1081 static PyObject*
1082 ceph_unregister_client(BaseMgrModule *self, PyObject *args)
1083 {
1084 char *addrs = nullptr;
1085 if (!PyArg_ParseTuple(args, "s:ceph_unregister_client", &addrs)) {
1086 return nullptr;
1087 }
1088 PyThreadState *tstate = PyEval_SaveThread();
1089 self->py_modules->unregister_client(self->this_module->get_name(), addrs);
1090 PyEval_RestoreThread(tstate);
1091 Py_RETURN_NONE;
1092 }
1093
1094 PyMethodDef BaseMgrModule_methods[] = {
1095 {"_ceph_get", (PyCFunction)ceph_state_get, METH_VARARGS,
1096 "Get a cluster object"},
1097
1098 {"_ceph_get_server", (PyCFunction)ceph_get_server, METH_VARARGS,
1099 "Get a server object"},
1100
1101 {"_ceph_get_metadata", (PyCFunction)get_metadata, METH_VARARGS,
1102 "Get a service's metadata"},
1103
1104 {"_ceph_get_daemon_status", (PyCFunction)get_daemon_status, METH_VARARGS,
1105 "Get a service's status"},
1106
1107 {"_ceph_send_command", (PyCFunction)ceph_send_command, METH_VARARGS,
1108 "Send a mon command"},
1109
1110 {"_ceph_set_health_checks", (PyCFunction)ceph_set_health_checks, METH_VARARGS,
1111 "Set health checks for this module"},
1112
1113 {"_ceph_get_mgr_id", (PyCFunction)ceph_get_mgr_id, METH_NOARGS,
1114 "Get the name of the Mgr daemon where we are running"},
1115
1116 {"_ceph_get_option", (PyCFunction)ceph_option_get, METH_VARARGS,
1117 "Get a native configuration option value"},
1118
1119 {"_ceph_get_module_option", (PyCFunction)ceph_get_module_option, METH_VARARGS,
1120 "Get a module configuration option value"},
1121
1122 {"_ceph_get_store_prefix", (PyCFunction)ceph_store_get_prefix, METH_VARARGS,
1123 "Get all KV store values with a given prefix"},
1124
1125 {"_ceph_set_module_option", (PyCFunction)ceph_set_module_option, METH_VARARGS,
1126 "Set a module configuration option value"},
1127
1128 {"_ceph_get_store", (PyCFunction)ceph_store_get, METH_VARARGS,
1129 "Get a stored field"},
1130
1131 {"_ceph_set_store", (PyCFunction)ceph_store_set, METH_VARARGS,
1132 "Set a stored field"},
1133
1134 {"_ceph_get_counter", (PyCFunction)get_counter, METH_VARARGS,
1135 "Get a performance counter"},
1136
1137 {"_ceph_get_latest_counter", (PyCFunction)get_latest_counter, METH_VARARGS,
1138 "Get the latest performance counter"},
1139
1140 {"_ceph_get_perf_schema", (PyCFunction)get_perf_schema, METH_VARARGS,
1141 "Get the performance counter schema"},
1142
1143 {"_ceph_log", (PyCFunction)ceph_log, METH_VARARGS,
1144 "Emit a (local) log message"},
1145
1146 {"_ceph_cluster_log", (PyCFunction)ceph_cluster_log, METH_VARARGS,
1147 "Emit a cluster log message"},
1148
1149 {"_ceph_get_version", (PyCFunction)ceph_get_version, METH_NOARGS,
1150 "Get the ceph version of this process"},
1151
1152 {"_ceph_get_release_name", (PyCFunction)ceph_get_release_name, METH_NOARGS,
1153 "Get the ceph release name of this process"},
1154
1155 {"_ceph_get_context", (PyCFunction)ceph_get_context, METH_NOARGS,
1156 "Get a CephContext* in a python capsule"},
1157
1158 {"_ceph_get_osdmap", (PyCFunction)ceph_get_osdmap, METH_NOARGS,
1159 "Get an OSDMap* in a python capsule"},
1160
1161 {"_ceph_set_uri", (PyCFunction)ceph_set_uri, METH_VARARGS,
1162 "Advertize a service URI served by this module"},
1163
1164 {"_ceph_have_mon_connection", (PyCFunction)ceph_have_mon_connection,
1165 METH_NOARGS, "Find out whether this mgr daemon currently has "
1166 "a connection to a monitor"},
1167
1168 {"_ceph_update_progress_event", (PyCFunction)ceph_update_progress_event,
1169 METH_VARARGS, "Update status of a progress event"},
1170 {"_ceph_complete_progress_event", (PyCFunction)ceph_complete_progress_event,
1171 METH_VARARGS, "Complete a progress event"},
1172 {"_ceph_clear_all_progress_events", (PyCFunction)ceph_clear_all_progress_events,
1173 METH_NOARGS, "Clear all progress events"},
1174
1175 {"_ceph_dispatch_remote", (PyCFunction)ceph_dispatch_remote,
1176 METH_VARARGS, "Dispatch a call to another module"},
1177
1178 {"_ceph_add_osd_perf_query", (PyCFunction)ceph_add_osd_perf_query,
1179 METH_VARARGS, "Add an osd perf query"},
1180
1181 {"_ceph_remove_osd_perf_query", (PyCFunction)ceph_remove_osd_perf_query,
1182 METH_VARARGS, "Remove an osd perf query"},
1183
1184 {"_ceph_get_osd_perf_counters", (PyCFunction)ceph_get_osd_perf_counters,
1185 METH_VARARGS, "Get osd perf counters"},
1186
1187 {"_ceph_is_authorized", (PyCFunction)ceph_is_authorized,
1188 METH_VARARGS, "Verify the current session caps are valid"},
1189
1190 {"_ceph_register_client", (PyCFunction)ceph_register_client,
1191 METH_VARARGS, "Register RADOS instance for potential blacklisting"},
1192
1193 {"_ceph_unregister_client", (PyCFunction)ceph_unregister_client,
1194 METH_VARARGS, "Unregister RADOS instance for potential blacklisting"},
1195
1196 {NULL, NULL, 0, NULL}
1197 };
1198
1199
1200 static PyObject *
1201 BaseMgrModule_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1202 {
1203 BaseMgrModule *self;
1204
1205 self = (BaseMgrModule *)type->tp_alloc(type, 0);
1206
1207 return (PyObject *)self;
1208 }
1209
1210 static int
1211 BaseMgrModule_init(BaseMgrModule *self, PyObject *args, PyObject *kwds)
1212 {
1213 PyObject *py_modules_capsule = nullptr;
1214 PyObject *this_module_capsule = nullptr;
1215 static const char *kwlist[] = {"py_modules", "this_module", NULL};
1216
1217 if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO",
1218 const_cast<char**>(kwlist),
1219 &py_modules_capsule,
1220 &this_module_capsule)) {
1221 return -1;
1222 }
1223
1224 self->py_modules = static_cast<ActivePyModules*>(PyCapsule_GetPointer(
1225 py_modules_capsule, nullptr));
1226 ceph_assert(self->py_modules);
1227 self->this_module = static_cast<ActivePyModule*>(PyCapsule_GetPointer(
1228 this_module_capsule, nullptr));
1229 ceph_assert(self->this_module);
1230
1231 return 0;
1232 }
1233
1234 PyTypeObject BaseMgrModuleType = {
1235 PyVarObject_HEAD_INIT(NULL, 0)
1236 "ceph_module.BaseMgrModule", /* tp_name */
1237 sizeof(BaseMgrModule), /* tp_basicsize */
1238 0, /* tp_itemsize */
1239 0, /* tp_dealloc */
1240 0, /* tp_print */
1241 0, /* tp_getattr */
1242 0, /* tp_setattr */
1243 0, /* tp_compare */
1244 0, /* tp_repr */
1245 0, /* tp_as_number */
1246 0, /* tp_as_sequence */
1247 0, /* tp_as_mapping */
1248 0, /* tp_hash */
1249 0, /* tp_call */
1250 0, /* tp_str */
1251 0, /* tp_getattro */
1252 0, /* tp_setattro */
1253 0, /* tp_as_buffer */
1254 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
1255 "ceph-mgr Python Plugin", /* tp_doc */
1256 0, /* tp_traverse */
1257 0, /* tp_clear */
1258 0, /* tp_richcompare */
1259 0, /* tp_weaklistoffset */
1260 0, /* tp_iter */
1261 0, /* tp_iternext */
1262 BaseMgrModule_methods, /* tp_methods */
1263 0, /* tp_members */
1264 0, /* tp_getset */
1265 0, /* tp_base */
1266 0, /* tp_dict */
1267 0, /* tp_descr_get */
1268 0, /* tp_descr_set */
1269 0, /* tp_dictoffset */
1270 (initproc)BaseMgrModule_init, /* tp_init */
1271 0, /* tp_alloc */
1272 BaseMgrModule_new, /* tp_new */
1273 };
1274