1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2016 John Spray <john.spray@redhat.com>
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.
15 * The interface we present to python code that runs within
21 #include "mon/MonClient.h"
22 #include "common/errno.h"
23 #include "common/version.h"
28 #define dout_context g_ceph_context
29 #define dout_subsys ceph_subsys_mgr
31 PyModules
*global_handle
= NULL
;
34 class MonCommandCompletion
: public Context
36 PyObject
*python_completion
;
37 const std::string tag
;
38 PyThreadState
*pThreadState
;
44 MonCommandCompletion(PyObject
* ev
, const std::string
&tag_
, PyThreadState
*ts_
)
45 : python_completion(ev
), tag(tag_
), pThreadState(ts_
)
47 assert(python_completion
!= nullptr);
48 Py_INCREF(python_completion
);
51 ~MonCommandCompletion() override
53 Py_DECREF(python_completion
);
56 void finish(int r
) override
58 dout(10) << "MonCommandCompletion::finish()" << dendl
;
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);
65 auto set_fn
= PyObject_GetAttrString(python_completion
, "complete");
66 assert(set_fn
!= nullptr);
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
);
76 auto rtn
= PyObject_CallObject(set_fn
, args
);
82 global_handle
->notify_all("command", tag
);
88 ceph_send_command(PyObject
*self
, PyObject
*args
)
90 char *handle
= nullptr;
95 // Like "23" for an OSD or "myid" for an MDS
98 char *cmd_json
= nullptr;
100 PyObject
*completion
= nullptr;
101 if (!PyArg_ParseTuple(args
, "sOssss:ceph_send_command",
102 &handle
, &completion
, &type
, &name
, &cmd_json
, &tag
)) {
106 auto set_fn
= PyObject_GetAttrString(completion
, "complete");
107 if (set_fn
== nullptr) {
108 ceph_abort(); // TODO raise python exception instead
110 assert(PyCallable_Check(set_fn
));
114 auto c
= new MonCommandCompletion(completion
, tag
, PyThreadState_Get());
115 if (std::string(type
) == "mon") {
116 global_handle
->get_monc().start_mon_command(
122 } else if (std::string(type
) == "osd") {
124 uint64_t osd_id
= strict_strtoll(name
, 10, &err
);
127 string
msg("invalid osd_id: ");
128 msg
.append("\"").append(name
).append("\"");
129 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
134 global_handle
->get_objecter().osd_command(
142 } else if (std::string(type
) == "mds") {
143 int r
= global_handle
->get_client().mds_command(
151 string
msg("failed to send command to mds: ");
152 msg
.append(cpp_strerror(r
));
153 PyErr_SetString(PyExc_RuntimeError
, msg
.c_str());
156 } else if (std::string(type
) == "pg") {
158 if (!pgid
.parse(name
)) {
160 string
msg("invalid pgid: ");
161 msg
.append("\"").append(name
).append("\"");
162 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
167 global_handle
->get_objecter().pg_command(
178 string
msg("unknown service type: ");
180 PyErr_SetString(PyExc_ValueError
, msg
.c_str());
188 ceph_set_health_checks(PyObject
*self
, PyObject
*args
)
190 char *handle
= nullptr;
191 PyObject
*checks
= NULL
;
192 if (!PyArg_ParseTuple(args
, "sO:ceph_set_health_checks", &handle
, &checks
)) {
195 if (!PyDict_Check(checks
)) {
196 derr
<< __func__
<< " arg not a dict" << dendl
;
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
;
210 if (!PyDict_Check(check_info
)) {
211 derr
<< __func__
<< " item " << i
<< " " << check_name
212 << " value not a dict" << dendl
;
215 health_status_t severity
= HEALTH_OK
;
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
;
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
;
234 if (ks
== "severity") {
235 if (!PyString_Check(v
)) {
236 derr
<< __func__
<< " check " << check_name
237 << " severity value not string" << dendl
;
240 string
vs(PyString_AsString(v
));
241 if (vs
== "warning") {
242 severity
= HEALTH_WARN
;
243 } else if (vs
== "error") {
244 severity
= HEALTH_ERR
;
246 } else if (ks
== "summary") {
247 if (!PyString_Check(v
)) {
248 derr
<< __func__
<< " check " << check_name
249 << " summary value not string" << dendl
;
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
;
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
;
266 detail
.push_back(PyString_AsString(di
));
269 derr
<< __func__
<< " check " << check_name
270 << " unexpected key " << k
<< dendl
;
273 auto& d
= out_checks
.add(check_name
, severity
, summary
);
274 d
.detail
.swap(detail
);
277 JSONFormatter
jf(true);
278 dout(10) << "module " << handle
<< " health checks:\n";
279 out_checks
.dump(&jf
);
283 global_handle
->set_health_checks(handle
, std::move(out_checks
));
290 ceph_state_get(PyObject
*self
, PyObject
*args
)
292 char *handle
= nullptr;
294 if (!PyArg_ParseTuple(args
, "ss:ceph_state_get", &handle
, &what
)) {
298 return global_handle
->get_python(what
);
303 ceph_get_server(PyObject
*self
, PyObject
*args
)
305 char *handle
= nullptr;
306 char *hostname
= NULL
;
307 if (!PyArg_ParseTuple(args
, "sz:ceph_get_server", &handle
, &hostname
)) {
312 return global_handle
->get_server_python(hostname
);
314 return global_handle
->list_servers_python();
319 ceph_get_mgr_id(PyObject
*self
, PyObject
*args
)
321 return PyString_FromString(g_conf
->name
.get_id().c_str());
325 ceph_config_get(PyObject
*self
, PyObject
*args
)
327 char *handle
= nullptr;
328 char *what
= nullptr;
329 if (!PyArg_ParseTuple(args
, "ss:ceph_config_get", &handle
, &what
)) {
330 derr
<< "Invalid args!" << dendl
;
335 bool found
= global_handle
->get_config(handle
, what
, &value
);
337 dout(10) << "ceph_config_get " << what
<< " found: " << value
.c_str() << dendl
;
338 return PyString_FromString(value
.c_str());
340 dout(4) << "ceph_config_get " << what
<< " not found " << dendl
;
346 ceph_config_get_prefix(PyObject
*self
, PyObject
*args
)
348 char *handle
= nullptr;
349 char *prefix
= nullptr;
350 if (!PyArg_ParseTuple(args
, "ss:ceph_config_get", &handle
, &prefix
)) {
351 derr
<< "Invalid args!" << dendl
;
355 return global_handle
->get_config_prefix(handle
, prefix
);
359 ceph_config_set(PyObject
*self
, PyObject
*args
)
361 char *handle
= nullptr;
363 char *value
= nullptr;
364 if (!PyArg_ParseTuple(args
, "ssz:ceph_config_set", &handle
, &key
, &value
)) {
367 boost::optional
<string
> val
;
371 global_handle
->set_config(handle
, key
, val
);
377 get_metadata(PyObject
*self
, PyObject
*args
)
379 char *handle
= nullptr;
380 char *svc_name
= NULL
;
382 if (!PyArg_ParseTuple(args
, "sss:get_metadata", &handle
, &svc_name
, &svc_id
)) {
385 return global_handle
->get_metadata_python(handle
, svc_name
, svc_id
);
389 get_daemon_status(PyObject
*self
, PyObject
*args
)
391 char *handle
= nullptr;
392 char *svc_name
= NULL
;
394 if (!PyArg_ParseTuple(args
, "sss:get_daemon_status", &handle
, &svc_name
,
398 return global_handle
->get_daemon_status_python(handle
, svc_name
, svc_id
);
402 ceph_log(PyObject
*self
, PyObject
*args
)
405 char *record
= nullptr;
406 char *handle
= nullptr;
407 if (!PyArg_ParseTuple(args
, "sis:log", &handle
, &level
, &record
)) {
411 global_handle
->log(handle
, level
, record
);
417 ceph_get_version(PyObject
*self
, PyObject
*args
)
419 return PyString_FromString(pretty_version_to_str().c_str());
423 ceph_get_context(PyObject
*self
, PyObject
*args
)
425 return global_handle
->get_context();
429 get_counter(PyObject
*self
, PyObject
*args
)
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
)) {
439 return global_handle
->get_counter_python(
440 handle
, svc_name
, svc_id
, counter_path
);
444 get_perf_schema(PyObject
*self
, PyObject
*args
)
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
,
454 return global_handle
->get_perf_schema_python(handle
, type_str
, svc_id
);
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
,
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
}