]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/BaseMgrModule.cc
update sources to v12.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"
28
29#include "BaseMgrModule.h"
30#include "Gil.h"
31
32#define dout_context g_ceph_context
33#define dout_subsys ceph_subsys_mgr
34
35#define PLACEHOLDER ""
36
37
38typedef struct {
39 PyObject_HEAD
40 ActivePyModules *py_modules;
41 ActivePyModule *this_module;
42} BaseMgrModule;
43
44class MonCommandCompletion : public Context
45{
46 ActivePyModules *py_modules;
47 PyObject *python_completion;
48 const std::string tag;
49 SafeThreadState pThreadState;
50
51public:
52 std::string outs;
53 bufferlist outbl;
54
55 MonCommandCompletion(
56 ActivePyModules *py_modules_, PyObject* ev,
57 const std::string &tag_, PyThreadState *ts_)
58 : py_modules(py_modules_), python_completion(ev),
59 tag(tag_), pThreadState(ts_)
60 {
61 assert(python_completion != nullptr);
62 Py_INCREF(python_completion);
63 }
64
65 ~MonCommandCompletion() override
66 {
67 if (python_completion) {
68 // Usually do this in finish(): this path is only for if we're
69 // being destroyed without completing.
70 Gil gil(pThreadState, true);
71 Py_DECREF(python_completion);
72 python_completion = nullptr;
73 }
74 }
75
76 void finish(int r) override
77 {
78 assert(python_completion != nullptr);
79
80 dout(10) << "MonCommandCompletion::finish()" << dendl;
81 {
82 // Scoped so the Gil is released before calling notify_all()
83 // Create new thread state because this is called via the MonClient
84 // Finisher, not the PyModules finisher.
85 Gil gil(pThreadState, true);
86
87 auto set_fn = PyObject_GetAttrString(python_completion, "complete");
88 assert(set_fn != nullptr);
89
90 auto pyR = PyInt_FromLong(r);
91 auto pyOutBl = PyString_FromString(outbl.to_str().c_str());
92 auto pyOutS = PyString_FromString(outs.c_str());
93 auto args = PyTuple_Pack(3, pyR, pyOutBl, pyOutS);
94 Py_DECREF(pyR);
95 Py_DECREF(pyOutBl);
96 Py_DECREF(pyOutS);
97
98 auto rtn = PyObject_CallObject(set_fn, args);
99 if (rtn != nullptr) {
100 Py_DECREF(rtn);
101 }
102 Py_DECREF(args);
103 Py_DECREF(set_fn);
104
105 Py_DECREF(python_completion);
106 python_completion = nullptr;
107 }
108 py_modules->notify_all("command", tag);
109 }
110};
111
112
113static PyObject*
114ceph_send_command(BaseMgrModule *self, PyObject *args)
115{
116 // Like mon, osd, mds
117 char *type = nullptr;
118
119 // Like "23" for an OSD or "myid" for an MDS
120 char *name = nullptr;
121
122 char *cmd_json = nullptr;
123 char *tag = nullptr;
124 PyObject *completion = nullptr;
125 if (!PyArg_ParseTuple(args, "Ossss:ceph_send_command",
126 &completion, &type, &name, &cmd_json, &tag)) {
127 return nullptr;
128 }
129
130 auto set_fn = PyObject_GetAttrString(completion, "complete");
131 if (set_fn == nullptr) {
132 ceph_abort(); // TODO raise python exception instead
133 } else {
134 assert(PyCallable_Check(set_fn));
135 }
136 Py_DECREF(set_fn);
137
138 auto c = new MonCommandCompletion(self->py_modules,
139 completion, tag, PyThreadState_Get());
140 if (std::string(type) == "mon") {
141 self->py_modules->get_monc().start_mon_command(
142 {cmd_json},
143 {},
144 &c->outbl,
145 &c->outs,
146 c);
147 } else if (std::string(type) == "osd") {
148 std::string err;
149 uint64_t osd_id = strict_strtoll(name, 10, &err);
150 if (!err.empty()) {
151 delete c;
152 string msg("invalid osd_id: ");
153 msg.append("\"").append(name).append("\"");
154 PyErr_SetString(PyExc_ValueError, msg.c_str());
155 return nullptr;
156 }
157
158 ceph_tid_t tid;
159 self->py_modules->get_objecter().osd_command(
160 osd_id,
161 {cmd_json},
162 {},
163 &tid,
164 &c->outbl,
165 &c->outs,
166 c);
167 } else if (std::string(type) == "mds") {
168 int r = self->py_modules->get_client().mds_command(
169 name,
170 {cmd_json},
171 {},
172 &c->outbl,
173 &c->outs,
174 c);
175 if (r != 0) {
176 string msg("failed to send command to mds: ");
177 msg.append(cpp_strerror(r));
178 PyErr_SetString(PyExc_RuntimeError, msg.c_str());
179 return nullptr;
180 }
181 } else if (std::string(type) == "pg") {
182 pg_t pgid;
183 if (!pgid.parse(name)) {
184 delete c;
185 string msg("invalid pgid: ");
186 msg.append("\"").append(name).append("\"");
187 PyErr_SetString(PyExc_ValueError, msg.c_str());
188 return nullptr;
189 }
190
191 ceph_tid_t tid;
192 self->py_modules->get_objecter().pg_command(
193 pgid,
194 {cmd_json},
195 {},
196 &tid,
197 &c->outbl,
198 &c->outs,
199 c);
200 return nullptr;
201 } else {
202 delete c;
203 string msg("unknown service type: ");
204 msg.append(type);
205 PyErr_SetString(PyExc_ValueError, msg.c_str());
206 return nullptr;
207 }
208
209 Py_RETURN_NONE;
210}
211
212static PyObject*
213ceph_set_health_checks(BaseMgrModule *self, PyObject *args)
214{
215 PyObject *checks = NULL;
216 if (!PyArg_ParseTuple(args, "O:ceph_set_health_checks", &checks)) {
217 return NULL;
218 }
219 if (!PyDict_Check(checks)) {
220 derr << __func__ << " arg not a dict" << dendl;
221 Py_RETURN_NONE;
222 }
223 PyObject *checksls = PyDict_Items(checks);
224 health_check_map_t out_checks;
225 for (int i = 0; i < PyList_Size(checksls); ++i) {
226 PyObject *kv = PyList_GET_ITEM(checksls, i);
227 char *check_name = nullptr;
228 PyObject *check_info = nullptr;
229 if (!PyArg_ParseTuple(kv, "sO:pair", &check_name, &check_info)) {
230 derr << __func__ << " dict item " << i
231 << " not a size 2 tuple" << dendl;
232 continue;
233 }
234 if (!PyDict_Check(check_info)) {
235 derr << __func__ << " item " << i << " " << check_name
236 << " value not a dict" << dendl;
237 continue;
238 }
239 health_status_t severity = HEALTH_OK;
240 string summary;
241 list<string> detail;
242 PyObject *infols = PyDict_Items(check_info);
243 for (int j = 0; j < PyList_Size(infols); ++j) {
244 PyObject *pair = PyList_GET_ITEM(infols, j);
245 if (!PyTuple_Check(pair)) {
246 derr << __func__ << " item " << i << " pair " << j
247 << " not a tuple" << dendl;
248 continue;
249 }
250 char *k = nullptr;
251 PyObject *v = nullptr;
252 if (!PyArg_ParseTuple(pair, "sO:pair", &k, &v)) {
253 derr << __func__ << " item " << i << " pair " << j
254 << " not a size 2 tuple" << dendl;
255 continue;
256 }
257 string ks(k);
258 if (ks == "severity") {
259 if (!PyString_Check(v)) {
260 derr << __func__ << " check " << check_name
261 << " severity value not string" << dendl;
262 continue;
263 }
264 string vs(PyString_AsString(v));
265 if (vs == "warning") {
266 severity = HEALTH_WARN;
267 } else if (vs == "error") {
268 severity = HEALTH_ERR;
269 }
270 } else if (ks == "summary") {
271 if (!PyString_Check(v)) {
272 derr << __func__ << " check " << check_name
273 << " summary value not string" << dendl;
274 continue;
275 }
276 summary = PyString_AsString(v);
277 } else if (ks == "detail") {
278 if (!PyList_Check(v)) {
279 derr << __func__ << " check " << check_name
280 << " detail value not list" << dendl;
281 continue;
282 }
283 for (int k = 0; k < PyList_Size(v); ++k) {
284 PyObject *di = PyList_GET_ITEM(v, k);
285 if (!PyString_Check(di)) {
286 derr << __func__ << " check " << check_name
287 << " detail item " << k << " not a string" << dendl;
288 continue;
289 }
290 detail.push_back(PyString_AsString(di));
291 }
292 } else {
293 derr << __func__ << " check " << check_name
294 << " unexpected key " << k << dendl;
295 }
296 }
297 auto& d = out_checks.add(check_name, severity, summary);
298 d.detail.swap(detail);
299 }
300
301 JSONFormatter jf(true);
302 dout(10) << "module " << self->this_module->get_name()
303 << " health checks:\n";
304 out_checks.dump(&jf);
305 jf.flush(*_dout);
306 *_dout << dendl;
307
308 PyThreadState *tstate = PyEval_SaveThread();
309 self->py_modules->set_health_checks(self->this_module->get_name(),
310 std::move(out_checks));
311 PyEval_RestoreThread(tstate);
312
313 Py_RETURN_NONE;
314}
315
316
317static PyObject*
318ceph_state_get(BaseMgrModule *self, PyObject *args)
319{
320 char *what = NULL;
321 if (!PyArg_ParseTuple(args, "s:ceph_state_get", &what)) {
322 return NULL;
323 }
324
325 return self->py_modules->get_python(what);
326}
327
328
329static PyObject*
330ceph_get_server(BaseMgrModule *self, PyObject *args)
331{
332 char *hostname = NULL;
333 if (!PyArg_ParseTuple(args, "z:ceph_get_server", &hostname)) {
334 return NULL;
335 }
336
337 if (hostname) {
338 return self->py_modules->get_server_python(hostname);
339 } else {
340 return self->py_modules->list_servers_python();
341 }
342}
343
344static PyObject*
345ceph_get_mgr_id(BaseMgrModule *self, PyObject *args)
346{
347 return PyString_FromString(g_conf->name.get_id().c_str());
348}
349
350static PyObject*
351ceph_config_get(BaseMgrModule *self, PyObject *args)
352{
353 char *what = nullptr;
354 if (!PyArg_ParseTuple(args, "s:ceph_config_get", &what)) {
355 derr << "Invalid args!" << dendl;
356 return nullptr;
357 }
358
359 std::string value;
360 bool found = self->py_modules->get_config(self->this_module->get_name(),
361 what, &value);
362 if (found) {
363 dout(10) << "ceph_config_get " << what << " found: " << value.c_str() << dendl;
364 return PyString_FromString(value.c_str());
365 } else {
366 dout(4) << "ceph_config_get " << what << " not found " << dendl;
367 Py_RETURN_NONE;
368 }
369}
370
371static PyObject*
372ceph_config_get_prefix(BaseMgrModule *self, PyObject *args)
373{
374 char *prefix = nullptr;
375 if (!PyArg_ParseTuple(args, "s:ceph_config_get", &prefix)) {
376 derr << "Invalid args!" << dendl;
377 return nullptr;
378 }
379
380 return self->py_modules->get_config_prefix(self->this_module->get_name(),
381 prefix);
382}
383
384static PyObject*
385ceph_config_set(BaseMgrModule *self, PyObject *args)
386{
387 char *key = nullptr;
388 char *value = nullptr;
389 if (!PyArg_ParseTuple(args, "sz:ceph_config_set", &key, &value)) {
390 return nullptr;
391 }
392 boost::optional<string> val;
393 if (value) {
394 val = value;
395 }
396 self->py_modules->set_config(self->this_module->get_name(), key, val);
397
398 Py_RETURN_NONE;
399}
400
401static PyObject*
402get_metadata(BaseMgrModule *self, PyObject *args)
403{
404 char *svc_name = NULL;
405 char *svc_id = NULL;
406 if (!PyArg_ParseTuple(args, "ss:get_metadata", &svc_name, &svc_id)) {
407 return nullptr;
408 }
409 return self->py_modules->get_metadata_python(svc_name, svc_id);
410}
411
412static PyObject*
413get_daemon_status(BaseMgrModule *self, PyObject *args)
414{
415 char *svc_name = NULL;
416 char *svc_id = NULL;
417 if (!PyArg_ParseTuple(args, "ss:get_daemon_status", &svc_name,
418 &svc_id)) {
419 return nullptr;
420 }
421 return self->py_modules->get_daemon_status_python(svc_name, svc_id);
422}
423
424static PyObject*
425ceph_log(BaseMgrModule *self, PyObject *args)
426{
427
428 int level = 0;
429 char *record = nullptr;
430 if (!PyArg_ParseTuple(args, "is:log", &level, &record)) {
431 return nullptr;
432 }
433
434 assert(self->this_module);
435
436 self->this_module->log(level, record);
437
438 Py_RETURN_NONE;
439}
440
441static PyObject *
442ceph_get_version(BaseMgrModule *self, PyObject *args)
443{
444 return PyString_FromString(pretty_version_to_str().c_str());
445}
446
447static PyObject *
448ceph_get_context(BaseMgrModule *self, PyObject *args)
449{
450 return self->py_modules->get_context();
451}
452
453static PyObject*
454get_counter(BaseMgrModule *self, PyObject *args)
455{
456 char *svc_name = nullptr;
457 char *svc_id = nullptr;
458 char *counter_path = nullptr;
459 if (!PyArg_ParseTuple(args, "sss:get_counter", &svc_name,
460 &svc_id, &counter_path)) {
461 return nullptr;
462 }
463 return self->py_modules->get_counter_python(
464 svc_name, svc_id, counter_path);
465}
466
467static PyObject*
468get_perf_schema(BaseMgrModule *self, PyObject *args)
469{
470 char *type_str = nullptr;
471 char *svc_id = nullptr;
472 if (!PyArg_ParseTuple(args, "ss:get_perf_schema", &type_str,
473 &svc_id)) {
474 return nullptr;
475 }
476
477 return self->py_modules->get_perf_schema_python(type_str, svc_id);
478}
479
480static PyObject *
481ceph_get_osdmap(BaseMgrModule *self, PyObject *args)
482{
483 return self->py_modules->get_osdmap();
484}
485
486static PyObject*
487ceph_set_uri(BaseMgrModule *self, PyObject *args)
488{
489 char *svc_str = nullptr;
490 if (!PyArg_ParseTuple(args, "s:ceph_advertize_service",
491 &svc_str)) {
492 return nullptr;
493 }
494
495 // We call down into PyModules even though we have a MgrPyModule
496 // reference here, because MgrPyModule's fields are protected
497 // by PyModules' lock.
498 PyThreadState *tstate = PyEval_SaveThread();
499 self->py_modules->set_uri(self->this_module->get_name(), svc_str);
500 PyEval_RestoreThread(tstate);
501
502 Py_RETURN_NONE;
503}
504
94b18763
FG
505static PyObject*
506ceph_have_mon_connection(BaseMgrModule *self, PyObject *args)
507{
508 if (self->py_modules->get_monc().is_connected()) {
509 Py_RETURN_TRUE;
510 } else {
511 Py_RETURN_FALSE;
512 }
513}
514
3efd9988
FG
515
516PyMethodDef BaseMgrModule_methods[] = {
517 {"_ceph_get", (PyCFunction)ceph_state_get, METH_VARARGS,
518 "Get a cluster object"},
519
520 {"_ceph_get_server", (PyCFunction)ceph_get_server, METH_VARARGS,
521 "Get a server object"},
522
523 {"_ceph_get_metadata", (PyCFunction)get_metadata, METH_VARARGS,
524 "Get a service's metadata"},
525
526 {"_ceph_get_daemon_status", (PyCFunction)get_daemon_status, METH_VARARGS,
527 "Get a service's status"},
528
529 {"_ceph_send_command", (PyCFunction)ceph_send_command, METH_VARARGS,
530 "Send a mon command"},
531
532 {"_ceph_set_health_checks", (PyCFunction)ceph_set_health_checks, METH_VARARGS,
533 "Set health checks for this module"},
534
535 {"_ceph_get_mgr_id", (PyCFunction)ceph_get_mgr_id, METH_NOARGS,
536 "Get the name of the Mgr daemon where we are running"},
537
538 {"_ceph_get_config", (PyCFunction)ceph_config_get, METH_VARARGS,
539 "Get a configuration value"},
540
541 {"_ceph_get_config_prefix", (PyCFunction)ceph_config_get_prefix, METH_VARARGS,
542 "Get all configuration values with a given prefix"},
543
544 {"_ceph_set_config", (PyCFunction)ceph_config_set, METH_VARARGS,
545 "Set a configuration value"},
546
547 {"_ceph_get_counter", (PyCFunction)get_counter, METH_VARARGS,
548 "Get a performance counter"},
549
550 {"_ceph_get_perf_schema", (PyCFunction)get_perf_schema, METH_VARARGS,
551 "Get the performance counter schema"},
552
553 {"_ceph_log", (PyCFunction)ceph_log, METH_VARARGS,
554 "Emit a (local) log message"},
555
556 {"_ceph_get_version", (PyCFunction)ceph_get_version, METH_VARARGS,
557 "Get the ceph version of this process"},
558
559 {"_ceph_get_context", (PyCFunction)ceph_get_context, METH_NOARGS,
560 "Get a CephContext* in a python capsule"},
561
562 {"_ceph_get_osdmap", (PyCFunction)ceph_get_osdmap, METH_NOARGS,
563 "Get an OSDMap* in a python capsule"},
564
565 {"_ceph_set_uri", (PyCFunction)ceph_set_uri, METH_VARARGS,
566 "Advertize a service URI served by this module"},
567
94b18763
FG
568 {"_ceph_have_mon_connection", (PyCFunction)ceph_have_mon_connection,
569 METH_NOARGS, "Find out whether this mgr daemon currently has "
570 "a connection to a monitor"},
571
3efd9988
FG
572 {NULL, NULL, 0, NULL}
573};
574
575
576static PyObject *
577BaseMgrModule_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
578{
579 BaseMgrModule *self;
580
581 self = (BaseMgrModule *)type->tp_alloc(type, 0);
582
583 return (PyObject *)self;
584}
585
586static int
587BaseMgrModule_init(BaseMgrModule *self, PyObject *args, PyObject *kwds)
588{
589 PyObject *py_modules_capsule = nullptr;
590 PyObject *this_module_capsule = nullptr;
591 static const char *kwlist[] = {"py_modules", "this_module", NULL};
592
593 if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO",
594 const_cast<char**>(kwlist),
595 &py_modules_capsule,
596 &this_module_capsule)) {
597 return -1;
598 }
599
600 self->py_modules = (ActivePyModules*)PyCapsule_GetPointer(
601 py_modules_capsule, nullptr);
602 assert(self->py_modules);
603 self->this_module = (ActivePyModule*)PyCapsule_GetPointer(
604 this_module_capsule, nullptr);
605 assert(self->this_module);
606
607 return 0;
608}
609
610PyTypeObject BaseMgrModuleType = {
611 PyVarObject_HEAD_INIT(NULL, 0)
612 "ceph_module.BaseMgrModule", /* tp_name */
613 sizeof(BaseMgrModule), /* tp_basicsize */
614 0, /* tp_itemsize */
615 0, /* tp_dealloc */
616 0, /* tp_print */
617 0, /* tp_getattr */
618 0, /* tp_setattr */
619 0, /* tp_compare */
620 0, /* tp_repr */
621 0, /* tp_as_number */
622 0, /* tp_as_sequence */
623 0, /* tp_as_mapping */
624 0, /* tp_hash */
625 0, /* tp_call */
626 0, /* tp_str */
627 0, /* tp_getattro */
628 0, /* tp_setattro */
629 0, /* tp_as_buffer */
630 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
631 "ceph-mgr Python Plugin", /* tp_doc */
632 0, /* tp_traverse */
633 0, /* tp_clear */
634 0, /* tp_richcompare */
635 0, /* tp_weaklistoffset */
636 0, /* tp_iter */
637 0, /* tp_iternext */
638 BaseMgrModule_methods, /* tp_methods */
639 0, /* tp_members */
640 0, /* tp_getset */
641 0, /* tp_base */
642 0, /* tp_dict */
643 0, /* tp_descr_get */
644 0, /* tp_descr_set */
645 0, /* tp_dictoffset */
646 (initproc)BaseMgrModule_init, /* tp_init */
647 0, /* tp_alloc */
648 BaseMgrModule_new, /* tp_new */
649};
650