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