]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/BaseMgrModule.cc
import ceph pacific 16.2.5
[ceph.git] / ceph / src / mgr / BaseMgrModule.cc
CommitLineData
3efd9988
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 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"
9f95a23c 28#include "mgr/Types.h"
3efd9988 29
9f95a23c 30#include "PyUtil.h"
3efd9988
FG
31#include "BaseMgrModule.h"
32#include "Gil.h"
33
11fdf7f2
TL
34#include <algorithm>
35
3efd9988
FG
36#define dout_context g_ceph_context
37#define dout_subsys ceph_subsys_mgr
38
39#define PLACEHOLDER ""
40
41
42typedef struct {
43 PyObject_HEAD
44 ActivePyModules *py_modules;
45 ActivePyModule *this_module;
46} BaseMgrModule;
47
48class MonCommandCompletion : public Context
49{
50 ActivePyModules *py_modules;
51 PyObject *python_completion;
52 const std::string tag;
53 SafeThreadState pThreadState;
54
55public:
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 {
11fdf7f2 65 ceph_assert(python_completion != nullptr);
3efd9988
FG
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 {
11fdf7f2 82 ceph_assert(python_completion != nullptr);
3efd9988
FG
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");
11fdf7f2 92 ceph_assert(set_fn != nullptr);
3efd9988 93
9f95a23c
TL
94 auto pyR = PyLong_FromLong(r);
95 auto pyOutBl = PyUnicode_FromString(outbl.to_str().c_str());
96 auto pyOutS = PyUnicode_FromString(outs.c_str());
3efd9988
FG
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
117static PyObject*
118ceph_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;
cd265ab1
TL
128 char *inbuf_ptr = nullptr;
129 Py_ssize_t inbuf_len = 0;
130 bufferlist inbuf = {};
131
3efd9988 132 PyObject *completion = nullptr;
cd265ab1
TL
133 if (!PyArg_ParseTuple(args, "Ossssz#:ceph_send_command",
134 &completion, &type, &name, &cmd_json, &tag, &inbuf_ptr, &inbuf_len)) {
3efd9988
FG
135 return nullptr;
136 }
137
cd265ab1
TL
138 if (inbuf_ptr) {
139 inbuf.append(inbuf_ptr, (unsigned)inbuf_len);
140 }
141
3efd9988
FG
142 auto set_fn = PyObject_GetAttrString(completion, "complete");
143 if (set_fn == nullptr) {
144 ceph_abort(); // TODO raise python exception instead
145 } else {
11fdf7f2 146 ceph_assert(PyCallable_Check(set_fn));
3efd9988
FG
147 }
148 Py_DECREF(set_fn);
149
11fdf7f2 150 MonCommandCompletion *command_c = new MonCommandCompletion(self->py_modules,
3efd9988 151 completion, tag, PyThreadState_Get());
a8e16298
TL
152
153 PyThreadState *tstate = PyEval_SaveThread();
154
3efd9988 155 if (std::string(type) == "mon") {
11fdf7f2
TL
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.
9f95a23c 164 auto c = new LambdaContext([command_c, self](int command_r){
11fdf7f2 165 self->py_modules->get_objecter().wait_for_latest_osdmap(
f67539c2
TL
166 [command_c, command_r](boost::system::error_code) {
167 command_c->complete(command_r);
168 });
11fdf7f2
TL
169 });
170
3efd9988 171 self->py_modules->get_monc().start_mon_command(
11fdf7f2 172 name,
3efd9988 173 {cmd_json},
cd265ab1 174 inbuf,
11fdf7f2
TL
175 &command_c->outbl,
176 &command_c->outs,
81eedcae 177 new C_OnFinisher(c, &self->py_modules->cmd_finisher));
3efd9988
FG
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()) {
11fdf7f2 182 delete command_c;
3efd9988
FG
183 string msg("invalid osd_id: ");
184 msg.append("\"").append(name).append("\"");
a8e16298 185 PyEval_RestoreThread(tstate);
3efd9988
FG
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},
cd265ab1 194 inbuf,
3efd9988 195 &tid,
f67539c2
TL
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 });
3efd9988
FG
202 } else if (std::string(type) == "mds") {
203 int r = self->py_modules->get_client().mds_command(
204 name,
205 {cmd_json},
cd265ab1 206 inbuf,
11fdf7f2
TL
207 &command_c->outbl,
208 &command_c->outs,
81eedcae 209 new C_OnFinisher(command_c, &self->py_modules->cmd_finisher));
3efd9988
FG
210 if (r != 0) {
211 string msg("failed to send command to mds: ");
212 msg.append(cpp_strerror(r));
a8e16298 213 PyEval_RestoreThread(tstate);
3efd9988
FG
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)) {
11fdf7f2 220 delete command_c;
3efd9988
FG
221 string msg("invalid pgid: ");
222 msg.append("\"").append(name).append("\"");
a8e16298 223 PyEval_RestoreThread(tstate);
3efd9988
FG
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},
cd265ab1 232 inbuf,
3efd9988 233 &tid,
f67539c2
TL
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 });
a8e16298 240 PyEval_RestoreThread(tstate);
3efd9988
FG
241 return nullptr;
242 } else {
11fdf7f2 243 delete command_c;
3efd9988
FG
244 string msg("unknown service type: ");
245 msg.append(type);
a8e16298 246 PyEval_RestoreThread(tstate);
3efd9988
FG
247 PyErr_SetString(PyExc_ValueError, msg.c_str());
248 return nullptr;
249 }
250
a8e16298 251 PyEval_RestoreThread(tstate);
3efd9988
FG
252 Py_RETURN_NONE;
253}
254
255static PyObject*
256ceph_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;
9f95a23c 285 int64_t count = 0;
3efd9988
FG
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") {
9f95a23c 303 if (!PyUnicode_Check(v)) {
3efd9988
FG
304 derr << __func__ << " check " << check_name
305 << " severity value not string" << dendl;
306 continue;
307 }
9f95a23c 308 if (const string vs = PyUnicode_AsUTF8(v); vs == "warning") {
3efd9988
FG
309 severity = HEALTH_WARN;
310 } else if (vs == "error") {
311 severity = HEALTH_ERR;
312 }
313 } else if (ks == "summary") {
9f95a23c 314 if (!PyUnicode_Check(v)) {
3efd9988 315 derr << __func__ << " check " << check_name
eafe8130 316 << " summary value not [unicode] string" << dendl;
3efd9988 317 continue;
9f95a23c
TL
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;
3efd9988 328 }
3efd9988
FG
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);
9f95a23c 337 if (!PyUnicode_Check(di)) {
3efd9988 338 derr << __func__ << " check " << check_name
eafe8130 339 << " detail item " << k << " not a [unicode] string" << dendl;
3efd9988 340 continue;
9f95a23c
TL
341 } else {
342 detail.push_back(PyUnicode_AsUTF8(di));
3efd9988 343 }
3efd9988
FG
344 }
345 } else {
346 derr << __func__ << " check " << check_name
347 << " unexpected key " << k << dendl;
348 }
349 }
9f95a23c 350 auto& d = out_checks.add(check_name, severity, summary, count);
3efd9988
FG
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);
cd265ab1 365
3efd9988
FG
366 Py_RETURN_NONE;
367}
368
369
370static PyObject*
371ceph_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
382static PyObject*
383ceph_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
397static PyObject*
398ceph_get_mgr_id(BaseMgrModule *self, PyObject *args)
399{
9f95a23c 400 return PyUnicode_FromString(g_conf()->name.get_id().c_str());
3efd9988
FG
401}
402
403static PyObject*
11fdf7f2 404ceph_option_get(BaseMgrModule *self, PyObject *args)
3efd9988
FG
405{
406 char *what = nullptr;
11fdf7f2 407 if (!PyArg_ParseTuple(args, "s:ceph_option_get", &what)) {
3efd9988
FG
408 derr << "Invalid args!" << dendl;
409 return nullptr;
410 }
411
9f95a23c
TL
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 }
11fdf7f2 426 dout(10) << "ceph_option_get " << what << " found: " << value << dendl;
9f95a23c 427 return get_python_typed_option_value(opt->type, value);
3efd9988 428 } else {
11fdf7f2 429 dout(4) << "ceph_option_get " << what << " not found " << dendl;
9f95a23c
TL
430 PyErr_Format(PyExc_KeyError, "option not found: %s", what);
431 return nullptr;
3efd9988
FG
432 }
433}
434
f67539c2
TL
435static PyObject*
436ceph_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
3efd9988 447static PyObject*
11fdf7f2
TL
448ceph_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
467static PyObject*
468ceph_store_get_prefix(BaseMgrModule *self, PyObject *args)
3efd9988
FG
469{
470 char *prefix = nullptr;
11fdf7f2 471 if (!PyArg_ParseTuple(args, "s:ceph_store_get_prefix", &prefix)) {
3efd9988
FG
472 derr << "Invalid args!" << dendl;
473 return nullptr;
474 }
475
11fdf7f2 476 return self->py_modules->get_store_prefix(self->this_module->get_name(),
3efd9988
FG
477 prefix);
478}
479
480static PyObject*
11fdf7f2 481ceph_set_module_option(BaseMgrModule *self, PyObject *args)
3efd9988 482{
11fdf7f2 483 char *module = nullptr;
3efd9988
FG
484 char *key = nullptr;
485 char *value = nullptr;
11fdf7f2
TL
486 if (!PyArg_ParseTuple(args, "ssz:ceph_set_module_option",
487 &module, &key, &value)) {
488 derr << "Invalid args!" << dendl;
3efd9988
FG
489 return nullptr;
490 }
491 boost::optional<string> val;
492 if (value) {
493 val = value;
494 }
81eedcae 495 PyThreadState *tstate = PyEval_SaveThread();
11fdf7f2 496 self->py_modules->set_config(module, key, val);
81eedcae 497 PyEval_RestoreThread(tstate);
11fdf7f2
TL
498
499 Py_RETURN_NONE;
500}
501
502static PyObject*
503ceph_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;
9f95a23c 516 return PyUnicode_FromString(value.c_str());
11fdf7f2
TL
517 } else {
518 dout(4) << "ceph_store_get " << what << " not found " << dendl;
519 Py_RETURN_NONE;
520 }
521}
522
523static PyObject*
524ceph_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 }
81eedcae 535 PyThreadState *tstate = PyEval_SaveThread();
11fdf7f2 536 self->py_modules->set_store(self->this_module->get_name(), key, val);
81eedcae 537 PyEval_RestoreThread(tstate);
3efd9988
FG
538
539 Py_RETURN_NONE;
540}
541
542static PyObject*
543get_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
553static PyObject*
554get_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
565static PyObject*
566ceph_log(BaseMgrModule *self, PyObject *args)
567{
3efd9988 568 char *record = nullptr;
9f95a23c 569 if (!PyArg_ParseTuple(args, "s:log", &record)) {
3efd9988
FG
570 return nullptr;
571 }
572
11fdf7f2 573 ceph_assert(self->this_module);
3efd9988 574
9f95a23c 575 self->this_module->log(record);
3efd9988
FG
576
577 Py_RETURN_NONE;
578}
579
11fdf7f2
TL
580static PyObject*
581ceph_cluster_log(BaseMgrModule *self, PyObject *args)
582{
583 int prio = 0;
584 char *channel = nullptr;
585 char *message = nullptr;
11fdf7f2
TL
586
587 if (!PyArg_ParseTuple(args, "sis:ceph_cluster_log", &channel, &prio, &message)) {
588 return nullptr;
589 }
590
11fdf7f2
TL
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
3efd9988
FG
598static PyObject *
599ceph_get_version(BaseMgrModule *self, PyObject *args)
600{
9f95a23c 601 return PyUnicode_FromString(pretty_version_to_str().c_str());
3efd9988
FG
602}
603
b3b6e05e
TL
604static PyObject *
605ceph_get_ceph_conf_path(BaseMgrModule *self, PyObject *args)
606{
607 return PyUnicode_FromString(g_conf().get_conf_path().c_str());
608}
609
3efd9988 610static PyObject *
92f5a8d4
TL
611ceph_get_release_name(BaseMgrModule *self, PyObject *args)
612{
9f95a23c 613 return PyUnicode_FromString(ceph_release_to_str());
92f5a8d4
TL
614}
615
f67539c2
TL
616static PyObject *
617ceph_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
92f5a8d4
TL
626static PyObject *
627ceph_get_context(BaseMgrModule *self)
3efd9988
FG
628{
629 return self->py_modules->get_context();
630}
631
632static PyObject*
633get_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
11fdf7f2
TL
646static PyObject*
647get_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
3efd9988
FG
660static PyObject*
661get_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
673static PyObject *
674ceph_get_osdmap(BaseMgrModule *self, PyObject *args)
675{
676 return self->py_modules->get_osdmap();
677}
678
679static PyObject*
680ceph_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
f67539c2
TL
698static PyObject*
699ceph_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
94b18763
FG
715static PyObject*
716ceph_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
11fdf7f2
TL
725static PyObject*
726ceph_update_progress_event(BaseMgrModule *self, PyObject *args)
727{
728 char *evid = nullptr;
729 char *desc = nullptr;
730 float progress = 0.0;
f67539c2
TL
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)) {
11fdf7f2
TL
734 return nullptr;
735 }
736
737 PyThreadState *tstate = PyEval_SaveThread();
f67539c2 738 self->py_modules->update_progress_event(evid, desc, progress, add_to_ceph_s);
11fdf7f2
TL
739 PyEval_RestoreThread(tstate);
740
741 Py_RETURN_NONE;
742}
743
744static PyObject*
745ceph_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
760static PyObject*
761ceph_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
772static PyObject *
773ceph_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
819static PyObject*
820ceph_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) {
9f95a23c 914 if (!PyUnicode_Check(param_value)) {
11fdf7f2
TL
915 derr << __func__ << " query " << query_param_name << " item " << j
916 << " contains invalid param " << param_name << dendl;
917 Py_RETURN_NONE;
918 }
9f95a23c 919 auto type = PyUnicode_AsUTF8(param_value);
11fdf7f2
TL
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) {
9f95a23c 928 if (!PyUnicode_Check(param_value)) {
11fdf7f2
TL
929 derr << __func__ << " query " << query_param_name << " item " << j
930 << " contains invalid param " << param_name << dendl;
931 Py_RETURN_NONE;
932 }
9f95a23c 933 d.regex_str = PyUnicode_AsUTF8(param_value);
11fdf7f2 934 try {
9f95a23c 935 d.regex = d.regex_str.c_str();
11fdf7f2
TL
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);
9f95a23c 968 if (!PyUnicode_Check(py_type)) {
11fdf7f2
TL
969 derr << __func__ << " query " << query_param_name << " item " << j
970 << " not a string" << dendl;
971 Py_RETURN_NONE;
972 }
9f95a23c 973 auto type = PyUnicode_AsUTF8(py_type);
11fdf7f2
TL
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) {
9f95a23c 1004 if (!PyUnicode_Check(limit_param_val)) {
11fdf7f2
TL
1005 derr << __func__ << " " << limit_param_name << " not a string"
1006 << dendl;
1007 Py_RETURN_NONE;
1008 }
9f95a23c 1009 auto order_by = PyUnicode_AsUTF8(limit_param_val);
11fdf7f2
TL
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) {
11fdf7f2 1018 if (!PyLong_Check(limit_param_val)) {
11fdf7f2
TL
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
1055static PyObject*
1056ceph_remove_osd_perf_query(BaseMgrModule *self, PyObject *args)
1057{
9f95a23c 1058 MetricQueryID query_id;
11fdf7f2
TL
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
1068static PyObject*
1069ceph_get_osd_perf_counters(BaseMgrModule *self, PyObject *args)
1070{
9f95a23c 1071 MetricQueryID query_id;
11fdf7f2
TL
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}
3efd9988 1079
f67539c2
TL
1080// MDS perf query interface -- mostly follows ceph_add_osd_perf_query()
1081// style
1082
1083static PyObject*
1084ceph_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
1315static PyObject*
1316ceph_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
1328static PyObject*
1329ceph_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
92f5a8d4
TL
1340static PyObject*
1341ceph_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
9f95a23c
TL
1369 PyThreadState *tstate = PyEval_SaveThread();
1370 bool r = self->this_module->is_authorized(arguments);
1371 PyEval_RestoreThread(tstate);
1372
1373 if (r) {
92f5a8d4
TL
1374 Py_RETURN_TRUE;
1375 }
92f5a8d4
TL
1376 Py_RETURN_FALSE;
1377}
1378
9f95a23c
TL
1379static PyObject*
1380ceph_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
1392static PyObject*
1393ceph_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
3efd9988
FG
1405PyMethodDef 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
b3b6e05e
TL
1427 {"_ceph_get_ceph_conf_path", (PyCFunction)ceph_get_ceph_conf_path, METH_NOARGS,
1428 "Get path to ceph.conf"},
1429
11fdf7f2
TL
1430 {"_ceph_get_option", (PyCFunction)ceph_option_get, METH_VARARGS,
1431 "Get a native configuration option value"},
1432
f67539c2
TL
1433 {"_ceph_get_foreign_option", (PyCFunction)ceph_foreign_option_get, METH_VARARGS,
1434 "Get a native configuration option value for another entity"},
1435
11fdf7f2
TL
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"},
3efd9988 1441
11fdf7f2
TL
1442 {"_ceph_set_module_option", (PyCFunction)ceph_set_module_option, METH_VARARGS,
1443 "Set a module configuration option value"},
3efd9988 1444
11fdf7f2
TL
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"},
3efd9988
FG
1450
1451 {"_ceph_get_counter", (PyCFunction)get_counter, METH_VARARGS,
1452 "Get a performance counter"},
1453
11fdf7f2
TL
1454 {"_ceph_get_latest_counter", (PyCFunction)get_latest_counter, METH_VARARGS,
1455 "Get the latest performance counter"},
1456
3efd9988
FG
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
11fdf7f2
TL
1463 {"_ceph_cluster_log", (PyCFunction)ceph_cluster_log, METH_VARARGS,
1464 "Emit a cluster log message"},
1465
92f5a8d4 1466 {"_ceph_get_version", (PyCFunction)ceph_get_version, METH_NOARGS,
3efd9988
FG
1467 "Get the ceph version of this process"},
1468
92f5a8d4
TL
1469 {"_ceph_get_release_name", (PyCFunction)ceph_get_release_name, METH_NOARGS,
1470 "Get the ceph release name of this process"},
1471
f67539c2
TL
1472 {"_ceph_lookup_release_name", (PyCFunction)ceph_lookup_release_name, METH_VARARGS,
1473 "Get the ceph release name for a given major number"},
1474
3efd9988
FG
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
f67539c2
TL
1484 {"_ceph_set_device_wear_level", (PyCFunction)ceph_set_wear_level, METH_VARARGS,
1485 "Set device wear_level value"},
1486
94b18763
FG
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
11fdf7f2
TL
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
f67539c2
TL
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
92f5a8d4
TL
1519 {"_ceph_is_authorized", (PyCFunction)ceph_is_authorized,
1520 METH_VARARGS, "Verify the current session caps are valid"},
1521
9f95a23c 1522 {"_ceph_register_client", (PyCFunction)ceph_register_client,
f67539c2 1523 METH_VARARGS, "Register RADOS instance for potential blocklisting"},
9f95a23c
TL
1524
1525 {"_ceph_unregister_client", (PyCFunction)ceph_unregister_client,
f67539c2 1526 METH_VARARGS, "Unregister RADOS instance for potential blocklisting"},
9f95a23c 1527
3efd9988
FG
1528 {NULL, NULL, 0, NULL}
1529};
1530
1531
1532static PyObject *
1533BaseMgrModule_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
1542static int
1543BaseMgrModule_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
11fdf7f2
TL
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);
3efd9988
FG
1562
1563 return 0;
1564}
1565
1566PyTypeObject 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