initial code repo
[stor4nfv.git] / src / ceph / src / mgr / BaseMgrModule.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2016 John Spray <john.spray@redhat.com>
7  *
8  * This is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License version 2.1, as published by the Free Software
11  * Foundation.  See file COPYING.
12  */
13
14 /**
15  * The interface we present to python code that runs within
16  * ceph-mgr.  This is implemented as a Python class from which
17  * all modules must inherit -- access to the Ceph state is then
18  * available as methods on that object.
19  */
20
21 #include "Python.h"
22
23 #include "Mgr.h"
24
25 #include "mon/MonClient.h"
26 #include "common/errno.h"
27 #include "common/version.h"
28
29 #include "BaseMgrModule.h"
30 #include "Gil.h"
31
32 #define dout_context g_ceph_context
33 #define dout_subsys ceph_subsys_mgr
34
35 #define PLACEHOLDER ""
36
37
38 typedef struct {
39   PyObject_HEAD
40   ActivePyModules *py_modules;
41   ActivePyModule *this_module;
42 } BaseMgrModule;
43
44 class MonCommandCompletion : public Context
45 {
46   ActivePyModules *py_modules;
47   PyObject *python_completion;
48   const std::string tag;
49   SafeThreadState pThreadState;
50
51 public:
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
113 static PyObject*
114 ceph_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
212 static PyObject*
213 ceph_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
317 static PyObject*
318 ceph_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
329 static PyObject*
330 ceph_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
344 static PyObject*
345 ceph_get_mgr_id(BaseMgrModule *self, PyObject *args)
346 {
347   return PyString_FromString(g_conf->name.get_id().c_str());
348 }
349
350 static PyObject*
351 ceph_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
371 static PyObject*
372 ceph_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
384 static PyObject*
385 ceph_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
401 static PyObject*
402 get_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
412 static PyObject*
413 get_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
424 static PyObject*
425 ceph_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
441 static PyObject *
442 ceph_get_version(BaseMgrModule *self, PyObject *args)
443 {
444   return PyString_FromString(pretty_version_to_str().c_str());
445 }
446
447 static PyObject *
448 ceph_get_context(BaseMgrModule *self, PyObject *args)
449 {
450   return self->py_modules->get_context();
451 }
452
453 static PyObject*
454 get_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
467 static PyObject*
468 get_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
480 static PyObject *
481 ceph_get_osdmap(BaseMgrModule *self, PyObject *args)
482 {
483   return self->py_modules->get_osdmap();
484 }
485
486 static PyObject*
487 ceph_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
505
506 PyMethodDef BaseMgrModule_methods[] = {
507   {"_ceph_get", (PyCFunction)ceph_state_get, METH_VARARGS,
508    "Get a cluster object"},
509
510   {"_ceph_get_server", (PyCFunction)ceph_get_server, METH_VARARGS,
511    "Get a server object"},
512
513   {"_ceph_get_metadata", (PyCFunction)get_metadata, METH_VARARGS,
514    "Get a service's metadata"},
515
516   {"_ceph_get_daemon_status", (PyCFunction)get_daemon_status, METH_VARARGS,
517    "Get a service's status"},
518
519   {"_ceph_send_command", (PyCFunction)ceph_send_command, METH_VARARGS,
520    "Send a mon command"},
521
522   {"_ceph_set_health_checks", (PyCFunction)ceph_set_health_checks, METH_VARARGS,
523    "Set health checks for this module"},
524
525   {"_ceph_get_mgr_id", (PyCFunction)ceph_get_mgr_id, METH_NOARGS,
526    "Get the name of the Mgr daemon where we are running"},
527
528   {"_ceph_get_config", (PyCFunction)ceph_config_get, METH_VARARGS,
529    "Get a configuration value"},
530
531   {"_ceph_get_config_prefix", (PyCFunction)ceph_config_get_prefix, METH_VARARGS,
532    "Get all configuration values with a given prefix"},
533
534   {"_ceph_set_config", (PyCFunction)ceph_config_set, METH_VARARGS,
535    "Set a configuration value"},
536
537   {"_ceph_get_counter", (PyCFunction)get_counter, METH_VARARGS,
538     "Get a performance counter"},
539
540   {"_ceph_get_perf_schema", (PyCFunction)get_perf_schema, METH_VARARGS,
541     "Get the performance counter schema"},
542
543   {"_ceph_log", (PyCFunction)ceph_log, METH_VARARGS,
544    "Emit a (local) log message"},
545
546   {"_ceph_get_version", (PyCFunction)ceph_get_version, METH_VARARGS,
547    "Get the ceph version of this process"},
548
549   {"_ceph_get_context", (PyCFunction)ceph_get_context, METH_NOARGS,
550     "Get a CephContext* in a python capsule"},
551
552   {"_ceph_get_osdmap", (PyCFunction)ceph_get_osdmap, METH_NOARGS,
553     "Get an OSDMap* in a python capsule"},
554
555   {"_ceph_set_uri", (PyCFunction)ceph_set_uri, METH_VARARGS,
556     "Advertize a service URI served by this module"},
557
558   {NULL, NULL, 0, NULL}
559 };
560
561
562 static PyObject *
563 BaseMgrModule_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
564 {
565     BaseMgrModule *self;
566
567     self = (BaseMgrModule *)type->tp_alloc(type, 0);
568
569     return (PyObject *)self;
570 }
571
572 static int
573 BaseMgrModule_init(BaseMgrModule *self, PyObject *args, PyObject *kwds)
574 {
575     PyObject *py_modules_capsule = nullptr;
576     PyObject *this_module_capsule = nullptr;
577     static const char *kwlist[] = {"py_modules", "this_module", NULL};
578
579     if (! PyArg_ParseTupleAndKeywords(args, kwds, "OO",
580                                       const_cast<char**>(kwlist),
581                                       &py_modules_capsule,
582                                       &this_module_capsule)) {
583         return -1;
584     }
585
586     self->py_modules = (ActivePyModules*)PyCapsule_GetPointer(
587         py_modules_capsule, nullptr);
588     assert(self->py_modules);
589     self->this_module = (ActivePyModule*)PyCapsule_GetPointer(
590         this_module_capsule, nullptr);
591     assert(self->this_module);
592
593     return 0;
594 }
595
596 PyTypeObject BaseMgrModuleType = {
597   PyVarObject_HEAD_INIT(NULL, 0)
598   "ceph_module.BaseMgrModule", /* tp_name */
599   sizeof(BaseMgrModule),     /* tp_basicsize */
600   0,                         /* tp_itemsize */
601   0,                         /* tp_dealloc */
602   0,                         /* tp_print */
603   0,                         /* tp_getattr */
604   0,                         /* tp_setattr */
605   0,                         /* tp_compare */
606   0,                         /* tp_repr */
607   0,                         /* tp_as_number */
608   0,                         /* tp_as_sequence */
609   0,                         /* tp_as_mapping */
610   0,                         /* tp_hash */
611   0,                         /* tp_call */
612   0,                         /* tp_str */
613   0,                         /* tp_getattro */
614   0,                         /* tp_setattro */
615   0,                         /* tp_as_buffer */
616   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
617   "ceph-mgr Python Plugin", /* tp_doc */
618   0,                         /* tp_traverse */
619   0,                         /* tp_clear */
620   0,                         /* tp_richcompare */
621   0,                         /* tp_weaklistoffset */
622   0,                         /* tp_iter */
623   0,                         /* tp_iternext */
624   BaseMgrModule_methods,     /* tp_methods */
625   0,                         /* tp_members */
626   0,                         /* tp_getset */
627   0,                         /* tp_base */
628   0,                         /* tp_dict */
629   0,                         /* tp_descr_get */
630   0,                         /* tp_descr_set */
631   0,                         /* tp_dictoffset */
632   (initproc)BaseMgrModule_init,                         /* tp_init */
633   0,                         /* tp_alloc */
634   BaseMgrModule_new,     /* tp_new */
635 };
636