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