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