]> git.proxmox.com Git - ceph.git/blob - ceph/src/mgr/PyState.cc
c8d3c73ff803fbe2d52fb3c0546cb237c19ec5b0
[ceph.git] / ceph / src / mgr / PyState.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.
17 */
18
19 #include "Mgr.h"
20
21 #include "mon/MonClient.h"
22 #include "common/errno.h"
23 #include "common/version.h"
24
25 #include "PyState.h"
26 #include "Gil.h"
27
28 #define dout_context g_ceph_context
29 #define dout_subsys ceph_subsys_mgr
30
31 PyModules *global_handle = NULL;
32
33
34 class MonCommandCompletion : public Context
35 {
36 PyObject *python_completion;
37 const std::string tag;
38 PyThreadState *pThreadState;
39
40 public:
41 std::string outs;
42 bufferlist outbl;
43
44 MonCommandCompletion(PyObject* ev, const std::string &tag_, PyThreadState *ts_)
45 : python_completion(ev), tag(tag_), pThreadState(ts_)
46 {
47 assert(python_completion != nullptr);
48 Py_INCREF(python_completion);
49 }
50
51 ~MonCommandCompletion() override
52 {
53 Py_DECREF(python_completion);
54 }
55
56 void finish(int r) override
57 {
58 dout(10) << "MonCommandCompletion::finish()" << dendl;
59 {
60 // Scoped so the Gil is released before calling notify_all()
61 Gil gil(pThreadState);
62
63 auto set_fn = PyObject_GetAttrString(python_completion, "complete");
64 assert(set_fn != nullptr);
65
66 auto pyR = PyInt_FromLong(r);
67 auto pyOutBl = PyString_FromString(outbl.to_str().c_str());
68 auto pyOutS = PyString_FromString(outs.c_str());
69 auto args = PyTuple_Pack(3, pyR, pyOutBl, pyOutS);
70 Py_DECREF(pyR);
71 Py_DECREF(pyOutBl);
72 Py_DECREF(pyOutS);
73
74 auto rtn = PyObject_CallObject(set_fn, args);
75 if (rtn != nullptr) {
76 Py_DECREF(rtn);
77 }
78 Py_DECREF(args);
79 }
80 global_handle->notify_all("command", tag);
81 }
82 };
83
84
85 static PyObject*
86 ceph_send_command(PyObject *self, PyObject *args)
87 {
88 char *handle = nullptr;
89
90 // Like mon, osd, mds
91 char *type = nullptr;
92
93 // Like "23" for an OSD or "myid" for an MDS
94 char *name = nullptr;
95
96 char *cmd_json = nullptr;
97 char *tag = nullptr;
98 PyObject *completion = nullptr;
99 if (!PyArg_ParseTuple(args, "sOssss:ceph_send_command",
100 &handle, &completion, &type, &name, &cmd_json, &tag)) {
101 return nullptr;
102 }
103
104 auto set_fn = PyObject_GetAttrString(completion, "complete");
105 if (set_fn == nullptr) {
106 ceph_abort(); // TODO raise python exception instead
107 } else {
108 assert(PyCallable_Check(set_fn));
109 }
110 Py_DECREF(set_fn);
111
112 auto c = new MonCommandCompletion(completion, tag, PyThreadState_Get());
113 if (std::string(type) == "mon") {
114 global_handle->get_monc().start_mon_command(
115 {cmd_json},
116 {},
117 &c->outbl,
118 &c->outs,
119 c);
120 } else if (std::string(type) == "osd") {
121 std::string err;
122 uint64_t osd_id = strict_strtoll(name, 10, &err);
123 if (!err.empty()) {
124 delete c;
125 string msg("invalid osd_id: ");
126 msg.append("\"").append(name).append("\"");
127 PyErr_SetString(PyExc_ValueError, msg.c_str());
128 return nullptr;
129 }
130
131 ceph_tid_t tid;
132 global_handle->get_objecter().osd_command(
133 osd_id,
134 {cmd_json},
135 {},
136 &tid,
137 &c->outbl,
138 &c->outs,
139 c);
140 } else if (std::string(type) == "mds") {
141 int r = global_handle->get_client().mds_command(
142 name,
143 {cmd_json},
144 {},
145 &c->outbl,
146 &c->outs,
147 c);
148 if (r != 0) {
149 string msg("failed to send command to mds: ");
150 msg.append(cpp_strerror(r));
151 PyErr_SetString(PyExc_RuntimeError, msg.c_str());
152 return nullptr;
153 }
154 } else if (std::string(type) == "pg") {
155 pg_t pgid;
156 if (!pgid.parse(name)) {
157 delete c;
158 string msg("invalid pgid: ");
159 msg.append("\"").append(name).append("\"");
160 PyErr_SetString(PyExc_ValueError, msg.c_str());
161 return nullptr;
162 }
163
164 ceph_tid_t tid;
165 global_handle->get_objecter().pg_command(
166 pgid,
167 {cmd_json},
168 {},
169 &tid,
170 &c->outbl,
171 &c->outs,
172 c);
173 return nullptr;
174 } else {
175 delete c;
176 string msg("unknown service type: ");
177 msg.append(type);
178 PyErr_SetString(PyExc_ValueError, msg.c_str());
179 return nullptr;
180 }
181
182 Py_RETURN_NONE;
183 }
184
185 static PyObject*
186 ceph_set_health_checks(PyObject *self, PyObject *args)
187 {
188 char *handle = nullptr;
189 PyObject *checks = NULL;
190 if (!PyArg_ParseTuple(args, "sO:ceph_set_health_checks", &handle, &checks)) {
191 return NULL;
192 }
193 if (!PyDict_Check(checks)) {
194 derr << __func__ << " arg not a dict" << dendl;
195 Py_RETURN_NONE;
196 }
197 PyObject *checksls = PyDict_Items(checks);
198 health_check_map_t out_checks;
199 for (int i = 0; i < PyList_Size(checksls); ++i) {
200 PyObject *kv = PyList_GET_ITEM(checksls, i);
201 char *check_name = nullptr;
202 PyObject *check_info = nullptr;
203 if (!PyArg_ParseTuple(kv, "sO:pair", &check_name, &check_info)) {
204 derr << __func__ << " dict item " << i
205 << " not a size 2 tuple" << dendl;
206 continue;
207 }
208 if (!PyDict_Check(check_info)) {
209 derr << __func__ << " item " << i << " " << check_name
210 << " value not a dict" << dendl;
211 continue;
212 }
213 health_status_t severity = HEALTH_OK;
214 string summary;
215 list<string> detail;
216 PyObject *infols = PyDict_Items(check_info);
217 for (int j = 0; j < PyList_Size(infols); ++j) {
218 PyObject *pair = PyList_GET_ITEM(infols, j);
219 if (!PyTuple_Check(pair)) {
220 derr << __func__ << " item " << i << " pair " << j
221 << " not a tuple" << dendl;
222 continue;
223 }
224 char *k = nullptr;
225 PyObject *v = nullptr;
226 if (!PyArg_ParseTuple(pair, "sO:pair", &k, &v)) {
227 derr << __func__ << " item " << i << " pair " << j
228 << " not a size 2 tuple" << dendl;
229 continue;
230 }
231 string ks(k);
232 if (ks == "severity") {
233 if (!PyString_Check(v)) {
234 derr << __func__ << " check " << check_name
235 << " severity value not string" << dendl;
236 continue;
237 }
238 string vs(PyString_AsString(v));
239 if (vs == "warning") {
240 severity = HEALTH_WARN;
241 } else if (vs == "error") {
242 severity = HEALTH_ERR;
243 }
244 } else if (ks == "summary") {
245 if (!PyString_Check(v)) {
246 derr << __func__ << " check " << check_name
247 << " summary value not string" << dendl;
248 continue;
249 }
250 summary = PyString_AsString(v);
251 } else if (ks == "detail") {
252 if (!PyList_Check(v)) {
253 derr << __func__ << " check " << check_name
254 << " detail value not list" << dendl;
255 continue;
256 }
257 for (int k = 0; k < PyList_Size(v); ++k) {
258 PyObject *di = PyList_GET_ITEM(v, k);
259 if (!PyString_Check(di)) {
260 derr << __func__ << " check " << check_name
261 << " detail item " << k << " not a string" << dendl;
262 continue;
263 }
264 detail.push_back(PyString_AsString(di));
265 }
266 } else {
267 derr << __func__ << " check " << check_name
268 << " unexpected key " << k << dendl;
269 }
270 }
271 auto& d = out_checks.add(check_name, severity, summary);
272 d.detail.swap(detail);
273 }
274
275 JSONFormatter jf(true);
276 dout(10) << "module " << handle << " health checks:\n";
277 out_checks.dump(&jf);
278 jf.flush(*_dout);
279 *_dout << dendl;
280
281 global_handle->set_health_checks(handle, std::move(out_checks));
282
283 Py_RETURN_NONE;
284 }
285
286
287 static PyObject*
288 ceph_state_get(PyObject *self, PyObject *args)
289 {
290 char *handle = nullptr;
291 char *what = NULL;
292 if (!PyArg_ParseTuple(args, "ss:ceph_state_get", &handle, &what)) {
293 return NULL;
294 }
295
296 return global_handle->get_python(what);
297 }
298
299
300 static PyObject*
301 ceph_get_server(PyObject *self, PyObject *args)
302 {
303 char *handle = nullptr;
304 char *hostname = NULL;
305 if (!PyArg_ParseTuple(args, "sz:ceph_get_server", &handle, &hostname)) {
306 return NULL;
307 }
308
309 if (hostname) {
310 return global_handle->get_server_python(hostname);
311 } else {
312 return global_handle->list_servers_python();
313 }
314 }
315
316 static PyObject*
317 ceph_get_mgr_id(PyObject *self, PyObject *args)
318 {
319 return PyString_FromString(g_conf->name.get_id().c_str());
320 }
321
322 static PyObject*
323 ceph_config_get(PyObject *self, PyObject *args)
324 {
325 char *handle = nullptr;
326 char *what = nullptr;
327 if (!PyArg_ParseTuple(args, "ss:ceph_config_get", &handle, &what)) {
328 derr << "Invalid args!" << dendl;
329 return nullptr;
330 }
331
332 std::string value;
333 bool found = global_handle->get_config(handle, what, &value);
334 if (found) {
335 dout(10) << "ceph_config_get " << what << " found: " << value.c_str() << dendl;
336 return PyString_FromString(value.c_str());
337 } else {
338 dout(4) << "ceph_config_get " << what << " not found " << dendl;
339 Py_RETURN_NONE;
340 }
341 }
342
343 static PyObject*
344 ceph_config_get_prefix(PyObject *self, PyObject *args)
345 {
346 char *handle = nullptr;
347 char *prefix = nullptr;
348 if (!PyArg_ParseTuple(args, "ss:ceph_config_get", &handle, &prefix)) {
349 derr << "Invalid args!" << dendl;
350 return nullptr;
351 }
352
353 return global_handle->get_config_prefix(handle, prefix);
354 }
355
356 static PyObject*
357 ceph_config_set(PyObject *self, PyObject *args)
358 {
359 char *handle = nullptr;
360 char *key = nullptr;
361 char *value = nullptr;
362 if (!PyArg_ParseTuple(args, "sss:ceph_config_set", &handle, &key, &value)) {
363 return nullptr;
364 }
365
366 global_handle->set_config(handle, key, value);
367
368 Py_RETURN_NONE;
369 }
370
371 static PyObject*
372 get_metadata(PyObject *self, PyObject *args)
373 {
374 char *handle = nullptr;
375 char *svc_name = NULL;
376 char *svc_id = NULL;
377 if (!PyArg_ParseTuple(args, "sss:get_metadata", &handle, &svc_name, &svc_id)) {
378 return nullptr;
379 }
380 return global_handle->get_metadata_python(handle, svc_name, svc_id);
381 }
382
383 static PyObject*
384 get_daemon_status(PyObject *self, PyObject *args)
385 {
386 char *handle = nullptr;
387 char *svc_name = NULL;
388 char *svc_id = NULL;
389 if (!PyArg_ParseTuple(args, "sss:get_daemon_status", &handle, &svc_name,
390 &svc_id)) {
391 return nullptr;
392 }
393 return global_handle->get_daemon_status_python(handle, svc_name, svc_id);
394 }
395
396 static PyObject*
397 ceph_log(PyObject *self, PyObject *args)
398 {
399 int level = 0;
400 char *record = nullptr;
401 char *handle = nullptr;
402 if (!PyArg_ParseTuple(args, "sis:log", &handle, &level, &record)) {
403 return nullptr;
404 }
405
406 global_handle->log(handle, level, record);
407
408 Py_RETURN_NONE;
409 }
410
411 static PyObject *
412 ceph_get_version(PyObject *self, PyObject *args)
413 {
414 return PyString_FromString(pretty_version_to_str().c_str());
415 }
416
417 static PyObject *
418 ceph_get_context(PyObject *self, PyObject *args)
419 {
420 return global_handle->get_context();
421 }
422
423 static PyObject*
424 get_counter(PyObject *self, PyObject *args)
425 {
426 char *handle = nullptr;
427 char *svc_name = nullptr;
428 char *svc_id = nullptr;
429 char *counter_path = nullptr;
430 if (!PyArg_ParseTuple(args, "ssss:get_counter", &handle, &svc_name,
431 &svc_id, &counter_path)) {
432 return nullptr;
433 }
434 return global_handle->get_counter_python(
435 handle, svc_name, svc_id, counter_path);
436 }
437
438 static PyObject*
439 get_perf_schema(PyObject *self, PyObject *args)
440 {
441 char *handle = nullptr;
442 char *type_str = nullptr;
443 char *svc_id = nullptr;
444 if (!PyArg_ParseTuple(args, "sss:get_perf_schema", &handle, &type_str,
445 &svc_id)) {
446 return nullptr;
447 }
448
449 return global_handle->get_perf_schema_python(handle, type_str, svc_id);
450 }
451
452 PyMethodDef CephStateMethods[] = {
453 {"get", ceph_state_get, METH_VARARGS,
454 "Get a cluster object"},
455 {"get_server", ceph_get_server, METH_VARARGS,
456 "Get a server object"},
457 {"get_metadata", get_metadata, METH_VARARGS,
458 "Get a service's metadata"},
459 {"get_daemon_status", get_daemon_status, METH_VARARGS,
460 "Get a service's status"},
461 {"send_command", ceph_send_command, METH_VARARGS,
462 "Send a mon command"},
463 {"set_health_checks", ceph_set_health_checks, METH_VARARGS,
464 "Set health checks for this module"},
465 {"get_mgr_id", ceph_get_mgr_id, METH_NOARGS,
466 "Get the mgr id"},
467 {"get_config", ceph_config_get, METH_VARARGS,
468 "Get a configuration value"},
469 {"get_config_prefix", ceph_config_get_prefix, METH_VARARGS,
470 "Get all configuration values with a given prefix"},
471 {"set_config", ceph_config_set, METH_VARARGS,
472 "Set a configuration value"},
473 {"get_counter", get_counter, METH_VARARGS,
474 "Get a performance counter"},
475 {"get_perf_schema", get_perf_schema, METH_VARARGS,
476 "Get the performance counter schema"},
477 {"log", ceph_log, METH_VARARGS,
478 "Emit a (local) log message"},
479 {"get_version", ceph_get_version, METH_VARARGS,
480 "Get the ceph version of this process"},
481 {"get_context", ceph_get_context, METH_NOARGS,
482 "Get a CephContext* in a python capsule"},
483 {NULL, NULL, 0, NULL}
484 };
485