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