#include "Mgr.h"
#include "mon/MonClient.h"
+#include "common/errno.h"
#include "common/version.h"
#include "PyState.h"
+#include "Gil.h"
#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_mgr
PyModules *global_handle = NULL;
{
PyObject *python_completion;
const std::string tag;
+ PyThreadState *pThreadState;
public:
std::string outs;
bufferlist outbl;
- MonCommandCompletion(PyObject* ev, const std::string &tag_)
- : python_completion(ev), tag(tag_)
+ MonCommandCompletion(PyObject* ev, const std::string &tag_, PyThreadState *ts_)
+ : python_completion(ev), tag(tag_), pThreadState(ts_)
{
assert(python_completion != nullptr);
Py_INCREF(python_completion);
void finish(int r) override
{
- PyGILState_STATE gstate;
- gstate = PyGILState_Ensure();
-
- auto set_fn = PyObject_GetAttrString(python_completion, "complete");
- assert(set_fn != nullptr);
-
- auto pyR = PyInt_FromLong(r);
- auto pyOutBl = PyString_FromString(outbl.to_str().c_str());
- auto pyOutS = PyString_FromString(outs.c_str());
- auto args = PyTuple_Pack(3, pyR, pyOutBl, pyOutS);
- Py_DECREF(pyR);
- Py_DECREF(pyOutBl);
- Py_DECREF(pyOutS);
-
- auto rtn = PyObject_CallObject(set_fn, args);
- if (rtn != nullptr) {
- Py_DECREF(rtn);
+ dout(10) << "MonCommandCompletion::finish()" << dendl;
+ {
+ // Scoped so the Gil is released before calling notify_all()
+ // Create new thread state because this is called via the MonClient
+ // Finisher, not the PyModules finisher.
+ Gil gil(pThreadState, true);
+
+ auto set_fn = PyObject_GetAttrString(python_completion, "complete");
+ assert(set_fn != nullptr);
+
+ auto pyR = PyInt_FromLong(r);
+ auto pyOutBl = PyString_FromString(outbl.to_str().c_str());
+ auto pyOutS = PyString_FromString(outs.c_str());
+ auto args = PyTuple_Pack(3, pyR, pyOutBl, pyOutS);
+ Py_DECREF(pyR);
+ Py_DECREF(pyOutBl);
+ Py_DECREF(pyOutS);
+
+ auto rtn = PyObject_CallObject(set_fn, args);
+ if (rtn != nullptr) {
+ Py_DECREF(rtn);
+ }
+ Py_DECREF(args);
}
- Py_DECREF(args);
-
- PyGILState_Release(gstate);
-
global_handle->notify_all("command", tag);
}
};
}
Py_DECREF(set_fn);
- auto c = new MonCommandCompletion(completion, tag);
+ auto c = new MonCommandCompletion(completion, tag, PyThreadState_Get());
if (std::string(type) == "mon") {
global_handle->get_monc().start_mon_command(
{cmd_json},
std::string err;
uint64_t osd_id = strict_strtoll(name, 10, &err);
if (!err.empty()) {
- // TODO: raise exception
+ delete c;
+ string msg("invalid osd_id: ");
+ msg.append("\"").append(name).append("\"");
+ PyErr_SetString(PyExc_ValueError, msg.c_str());
return nullptr;
}
&c->outs,
c);
if (r != 0) {
- // TODO: raise exception
+ string msg("failed to send command to mds: ");
+ msg.append(cpp_strerror(r));
+ PyErr_SetString(PyExc_RuntimeError, msg.c_str());
return nullptr;
}
} else if (std::string(type) == "pg") {
- // TODO: expose objecter::pg_command
+ pg_t pgid;
+ if (!pgid.parse(name)) {
+ delete c;
+ string msg("invalid pgid: ");
+ msg.append("\"").append(name).append("\"");
+ PyErr_SetString(PyExc_ValueError, msg.c_str());
+ return nullptr;
+ }
+
+ ceph_tid_t tid;
+ global_handle->get_objecter().pg_command(
+ pgid,
+ {cmd_json},
+ {},
+ &tid,
+ &c->outbl,
+ &c->outs,
+ c);
return nullptr;
} else {
- // TODO: raise exception
+ delete c;
+ string msg("unknown service type: ");
+ msg.append(type);
+ PyErr_SetString(PyExc_ValueError, msg.c_str());
return nullptr;
}
Py_RETURN_NONE;
}
+static PyObject*
+ceph_set_health_checks(PyObject *self, PyObject *args)
+{
+ char *handle = nullptr;
+ PyObject *checks = NULL;
+ if (!PyArg_ParseTuple(args, "sO:ceph_set_health_checks", &handle, &checks)) {
+ return NULL;
+ }
+ if (!PyDict_Check(checks)) {
+ derr << __func__ << " arg not a dict" << dendl;
+ Py_RETURN_NONE;
+ }
+ PyObject *checksls = PyDict_Items(checks);
+ health_check_map_t out_checks;
+ for (int i = 0; i < PyList_Size(checksls); ++i) {
+ PyObject *kv = PyList_GET_ITEM(checksls, i);
+ char *check_name = nullptr;
+ PyObject *check_info = nullptr;
+ if (!PyArg_ParseTuple(kv, "sO:pair", &check_name, &check_info)) {
+ derr << __func__ << " dict item " << i
+ << " not a size 2 tuple" << dendl;
+ continue;
+ }
+ if (!PyDict_Check(check_info)) {
+ derr << __func__ << " item " << i << " " << check_name
+ << " value not a dict" << dendl;
+ continue;
+ }
+ health_status_t severity = HEALTH_OK;
+ string summary;
+ list<string> detail;
+ PyObject *infols = PyDict_Items(check_info);
+ for (int j = 0; j < PyList_Size(infols); ++j) {
+ PyObject *pair = PyList_GET_ITEM(infols, j);
+ if (!PyTuple_Check(pair)) {
+ derr << __func__ << " item " << i << " pair " << j
+ << " not a tuple" << dendl;
+ continue;
+ }
+ char *k = nullptr;
+ PyObject *v = nullptr;
+ if (!PyArg_ParseTuple(pair, "sO:pair", &k, &v)) {
+ derr << __func__ << " item " << i << " pair " << j
+ << " not a size 2 tuple" << dendl;
+ continue;
+ }
+ string ks(k);
+ if (ks == "severity") {
+ if (!PyString_Check(v)) {
+ derr << __func__ << " check " << check_name
+ << " severity value not string" << dendl;
+ continue;
+ }
+ string vs(PyString_AsString(v));
+ if (vs == "warning") {
+ severity = HEALTH_WARN;
+ } else if (vs == "error") {
+ severity = HEALTH_ERR;
+ }
+ } else if (ks == "summary") {
+ if (!PyString_Check(v)) {
+ derr << __func__ << " check " << check_name
+ << " summary value not string" << dendl;
+ continue;
+ }
+ summary = PyString_AsString(v);
+ } else if (ks == "detail") {
+ if (!PyList_Check(v)) {
+ derr << __func__ << " check " << check_name
+ << " detail value not list" << dendl;
+ continue;
+ }
+ for (int k = 0; k < PyList_Size(v); ++k) {
+ PyObject *di = PyList_GET_ITEM(v, k);
+ if (!PyString_Check(di)) {
+ derr << __func__ << " check " << check_name
+ << " detail item " << k << " not a string" << dendl;
+ continue;
+ }
+ detail.push_back(PyString_AsString(di));
+ }
+ } else {
+ derr << __func__ << " check " << check_name
+ << " unexpected key " << k << dendl;
+ }
+ }
+ auto& d = out_checks.add(check_name, severity, summary);
+ d.detail.swap(detail);
+ }
+
+ JSONFormatter jf(true);
+ dout(10) << "module " << handle << " health checks:\n";
+ out_checks.dump(&jf);
+ jf.flush(*_dout);
+ *_dout << dendl;
+
+ global_handle->set_health_checks(handle, std::move(out_checks));
+
+ Py_RETURN_NONE;
+}
+
static PyObject*
ceph_state_get(PyObject *self, PyObject *args)
}
}
+static PyObject*
+ceph_get_mgr_id(PyObject *self, PyObject *args)
+{
+ return PyString_FromString(g_conf->name.get_id().c_str());
+}
+
static PyObject*
ceph_config_get(PyObject *self, PyObject *args)
{
std::string value;
bool found = global_handle->get_config(handle, what, &value);
if (found) {
- derr << "Found" << dendl;
+ dout(10) << "ceph_config_get " << what << " found: " << value.c_str() << dendl;
return PyString_FromString(value.c_str());
} else {
- derr << "Not found" << dendl;
+ dout(4) << "ceph_config_get " << what << " not found " << dendl;
Py_RETURN_NONE;
}
}
static PyObject*
-ceph_config_set(PyObject *self, PyObject *args)
+ceph_config_get_prefix(PyObject *self, PyObject *args)
{
char *handle = nullptr;
- char *key = nullptr;
- char *value = nullptr;
- if (!PyArg_ParseTuple(args, "sss:ceph_config_set", &handle, &key, &value)) {
+ char *prefix = nullptr;
+ if (!PyArg_ParseTuple(args, "ss:ceph_config_get", &handle, &prefix)) {
+ derr << "Invalid args!" << dendl;
return nullptr;
}
- global_handle->set_config(handle, key, value);
-
- Py_RETURN_NONE;
+ return global_handle->get_config_prefix(handle, prefix);
}
-static entity_type_t svc_type_from_str(const std::string &type_str)
+static PyObject*
+ceph_config_set(PyObject *self, PyObject *args)
{
- if (type_str == std::string("mds")) {
- return CEPH_ENTITY_TYPE_MDS;
- } else if (type_str == std::string("osd")) {
- return CEPH_ENTITY_TYPE_OSD;
- } else if (type_str == std::string("mon")) {
- return CEPH_ENTITY_TYPE_MON;
- } else {
- return CEPH_ENTITY_TYPE_ANY;
+ char *handle = nullptr;
+ char *key = nullptr;
+ char *value = nullptr;
+ if (!PyArg_ParseTuple(args, "ssz:ceph_config_set", &handle, &key, &value)) {
+ return nullptr;
+ }
+ boost::optional<string> val;
+ if (value) {
+ val = value;
}
+ global_handle->set_config(handle, key, val);
+
+ Py_RETURN_NONE;
}
static PyObject*
get_metadata(PyObject *self, PyObject *args)
{
char *handle = nullptr;
- char *type_str = NULL;
+ char *svc_name = NULL;
char *svc_id = NULL;
- if (!PyArg_ParseTuple(args, "sss:get_metadata", &handle, &type_str, &svc_id)) {
+ if (!PyArg_ParseTuple(args, "sss:get_metadata", &handle, &svc_name, &svc_id)) {
return nullptr;
}
+ return global_handle->get_metadata_python(handle, svc_name, svc_id);
+}
- entity_type_t svc_type = svc_type_from_str(type_str);
- if (svc_type == CEPH_ENTITY_TYPE_ANY) {
- // FIXME: form a proper exception
+static PyObject*
+get_daemon_status(PyObject *self, PyObject *args)
+{
+ char *handle = nullptr;
+ char *svc_name = NULL;
+ char *svc_id = NULL;
+ if (!PyArg_ParseTuple(args, "sss:get_daemon_status", &handle, &svc_name,
+ &svc_id)) {
return nullptr;
}
-
-
- return global_handle->get_metadata_python(handle, svc_type, svc_id);
+ return global_handle->get_daemon_status_python(handle, svc_name, svc_id);
}
static PyObject*
get_counter(PyObject *self, PyObject *args)
{
char *handle = nullptr;
- char *type_str = nullptr;
+ char *svc_name = nullptr;
char *svc_id = nullptr;
char *counter_path = nullptr;
- if (!PyArg_ParseTuple(args, "ssss:get_counter", &handle, &type_str,
+ if (!PyArg_ParseTuple(args, "ssss:get_counter", &handle, &svc_name,
&svc_id, &counter_path)) {
return nullptr;
}
+ return global_handle->get_counter_python(
+ handle, svc_name, svc_id, counter_path);
+}
- entity_type_t svc_type = svc_type_from_str(type_str);
- if (svc_type == CEPH_ENTITY_TYPE_ANY) {
- // FIXME: form a proper exception
+static PyObject*
+get_perf_schema(PyObject *self, PyObject *args)
+{
+ char *handle = nullptr;
+ char *type_str = nullptr;
+ char *svc_id = nullptr;
+ if (!PyArg_ParseTuple(args, "sss:get_perf_schema", &handle, &type_str,
+ &svc_id)) {
return nullptr;
}
- return global_handle->get_counter_python(
- handle, svc_type, svc_id, counter_path);
+ return global_handle->get_perf_schema_python(handle, type_str, svc_id);
}
PyMethodDef CephStateMethods[] = {
"Get a server object"},
{"get_metadata", get_metadata, METH_VARARGS,
"Get a service's metadata"},
+ {"get_daemon_status", get_daemon_status, METH_VARARGS,
+ "Get a service's status"},
{"send_command", ceph_send_command, METH_VARARGS,
"Send a mon command"},
+ {"set_health_checks", ceph_set_health_checks, METH_VARARGS,
+ "Set health checks for this module"},
+ {"get_mgr_id", ceph_get_mgr_id, METH_NOARGS,
+ "Get the mgr id"},
{"get_config", ceph_config_get, METH_VARARGS,
"Get a configuration value"},
+ {"get_config_prefix", ceph_config_get_prefix, METH_VARARGS,
+ "Get all configuration values with a given prefix"},
{"set_config", ceph_config_set, METH_VARARGS,
"Set a configuration value"},
{"get_counter", get_counter, METH_VARARGS,
"Get a performance counter"},
+ {"get_perf_schema", get_perf_schema, METH_VARARGS,
+ "Get the performance counter schema"},
{"log", ceph_log, METH_VARARGS,
"Emit a (local) log message"},
{"get_version", ceph_get_version, METH_VARARGS,