]>
git.proxmox.com Git - ceph.git/blob - ceph/src/mgr/MgrPyModule.cc
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.
17 #include "PyFormatter.h"
19 #include "common/debug.h"
21 #include "MgrPyModule.h"
23 //XXX courtesy of http://stackoverflow.com/questions/1418015/how-to-get-python-exception-text
24 #include <boost/python.hpp>
25 #include "include/assert.h" // boost clobbers this
27 // decode a Python exception into a string
28 std::string
handle_pyerror()
30 using namespace boost::python
;
31 using namespace boost
;
33 PyObject
*exc
, *val
, *tb
;
34 object formatted_list
, formatted
;
35 PyErr_Fetch(&exc
, &val
, &tb
);
36 handle
<> hexc(exc
), hval(allow_null(val
)), htb(allow_null(tb
));
37 object
traceback(import("traceback"));
39 object
format_exception_only(traceback
.attr("format_exception_only"));
40 formatted_list
= format_exception_only(hexc
, hval
);
42 object
format_exception(traceback
.attr("format_exception"));
43 formatted_list
= format_exception(hexc
,hval
, htb
);
45 formatted
= str("").join(formatted_list
);
46 return extract
<std::string
>(formatted
);
49 #define dout_context g_ceph_context
50 #define dout_subsys ceph_subsys_mgr
53 #define dout_prefix *_dout << "mgr[py] "
56 PyObject
* log_write(PyObject
*, PyObject
* args
) {
58 if (PyArg_ParseTuple(args
, "s", &m
)) {
60 if (len
&& m
[len
-1] == '\n') {
63 dout(4) << m
<< dendl
;
68 PyObject
* log_flush(PyObject
*, PyObject
*){
72 static PyMethodDef log_methods
[] = {
73 {"write", log_write
, METH_VARARGS
, "write stdout and stderr"},
74 {"flush", log_flush
, METH_VARARGS
, "flush"},
75 {nullptr, nullptr, 0, nullptr}
80 #define dout_prefix *_dout << "mgr " << __func__ << " "
82 MgrPyModule::MgrPyModule(const std::string
&module_name_
, const std::string
&sys_path
, PyThreadState
*main_ts_
)
83 : module_name(module_name_
),
84 pClassInstance(nullptr),
85 pMainThreadState(main_ts_
)
87 assert(pMainThreadState
!= nullptr);
89 Gil
gil(pMainThreadState
);
91 pMyThreadState
= Py_NewInterpreter();
92 if (pMyThreadState
== nullptr) {
93 derr
<< "Failed to create python sub-interpreter for '" << module_name
<< '"' << dendl
;
95 // Some python modules do not cope with an unpopulated argv, so lets
96 // fake one. This step also picks up site-packages into sys.path.
97 const char *argv
[] = {"ceph-mgr"};
98 PySys_SetArgv(1, (char**)argv
);
100 if (g_conf
->daemonize
) {
101 auto py_logger
= Py_InitModule("ceph_logger", log_methods
);
102 #if PY_MAJOR_VERSION >= 3
103 PySys_SetObject("stderr", py_logger
);
104 PySys_SetObject("stdout", py_logger
);
106 PySys_SetObject(const_cast<char*>("stderr"), py_logger
);
107 PySys_SetObject(const_cast<char*>("stdout"), py_logger
);
110 // Populate python namespace with callable hooks
111 Py_InitModule("ceph_state", CephStateMethods
);
113 PySys_SetPath(const_cast<char*>(sys_path
.c_str()));
117 MgrPyModule::~MgrPyModule()
119 if (pMyThreadState
!= nullptr) {
120 Gil
gil(pMyThreadState
);
122 Py_XDECREF(pClassInstance
);
125 // Ideally, now, we'd be able to do this:
127 // Py_EndInterpreter(pMyThreadState);
128 // PyThreadState_Swap(pMainThreadState);
130 // Unfortunately, if the module has any other *python* threads active
131 // at this point, Py_EndInterpreter() will abort with:
133 // Fatal Python error: Py_EndInterpreter: not the last thread
135 // This can happen when using CherryPy in a module, becuase CherryPy
136 // runs an extra thread as a timeout monitor, which spends most of its
137 // life inside a time.sleep(60). Unless you are very, very lucky with
138 // the timing calling this destructor, that thread will still be stuck
139 // in a sleep, and Py_EndInterpreter() will abort.
141 // This could of course also happen with a poorly written module which
142 // made no attempt to clean up any additional threads it created.
144 // The safest thing to do is just not call Py_EndInterpreter(), and
145 // let Py_Finalize() kill everything after all modules are shut down.
150 int MgrPyModule::load()
152 if (pMyThreadState
== nullptr) {
153 derr
<< "No python sub-interpreter exists for module '" << module_name
<< "'" << dendl
;
157 Gil
gil(pMyThreadState
);
160 PyObject
*pName
= PyString_FromString(module_name
.c_str());
161 auto pModule
= PyImport_Import(pName
);
163 if (pModule
== nullptr) {
164 derr
<< "Module not found: '" << module_name
<< "'" << dendl
;
165 derr
<< handle_pyerror() << dendl
;
170 // TODO: let them call it what they want instead of just 'Module'
171 auto pClass
= PyObject_GetAttrString(pModule
, (const char*)"Module");
173 if (pClass
== nullptr) {
174 derr
<< "Class not found in module '" << module_name
<< "'" << dendl
;
175 derr
<< handle_pyerror() << dendl
;
180 // Just using the module name as the handle, replace with a
181 // uuidish thing if needed
182 auto pyHandle
= PyString_FromString(module_name
.c_str());
183 auto pArgs
= PyTuple_Pack(1, pyHandle
);
184 pClassInstance
= PyObject_CallObject(pClass
, pArgs
);
188 if (pClassInstance
== nullptr) {
189 derr
<< "Failed to construct class in '" << module_name
<< "'" << dendl
;
190 derr
<< handle_pyerror() << dendl
;
193 dout(1) << "Constructed class from module: " << module_name
<< dendl
;
196 return load_commands();
199 int MgrPyModule::serve()
201 assert(pClassInstance
!= nullptr);
203 // This method is called from a separate OS thread (i.e. a thread not
204 // created by Python), so tell Gil to wrap this in a new thread state.
205 Gil
gil(pMyThreadState
, true);
207 auto pValue
= PyObject_CallMethod(pClassInstance
,
208 const_cast<char*>("serve"), nullptr);
211 if (pValue
!= NULL
) {
214 derr
<< module_name
<< ".serve:" << dendl
;
215 derr
<< handle_pyerror() << dendl
;
222 // FIXME: DRY wrt serve
223 void MgrPyModule::shutdown()
225 assert(pClassInstance
!= nullptr);
227 Gil
gil(pMyThreadState
);
229 auto pValue
= PyObject_CallMethod(pClassInstance
,
230 const_cast<char*>("shutdown"), nullptr);
232 if (pValue
!= NULL
) {
235 derr
<< "Failed to invoke shutdown() on " << module_name
<< dendl
;
236 derr
<< handle_pyerror() << dendl
;
240 void MgrPyModule::notify(const std::string
¬ify_type
, const std::string
¬ify_id
)
242 assert(pClassInstance
!= nullptr);
244 Gil
gil(pMyThreadState
);
247 auto pValue
= PyObject_CallMethod(pClassInstance
,
248 const_cast<char*>("notify"), const_cast<char*>("(ss)"),
249 notify_type
.c_str(), notify_id
.c_str());
251 if (pValue
!= NULL
) {
254 derr
<< module_name
<< ".notify:" << dendl
;
255 derr
<< handle_pyerror() << dendl
;
256 // FIXME: callers can't be expected to handle a python module
257 // that has spontaneously broken, but Mgr() should provide
258 // a hook to unload misbehaving modules when they have an
259 // error somewhere like this
263 void MgrPyModule::notify_clog(const LogEntry
&log_entry
)
265 assert(pClassInstance
!= nullptr);
267 Gil
gil(pMyThreadState
);
269 // Construct python-ized LogEntry
272 auto py_log_entry
= f
.get();
275 auto pValue
= PyObject_CallMethod(pClassInstance
,
276 const_cast<char*>("notify"), const_cast<char*>("(sN)"),
277 "clog", py_log_entry
);
279 if (pValue
!= NULL
) {
282 derr
<< module_name
<< ".notify_clog:" << dendl
;
283 derr
<< handle_pyerror() << dendl
;
284 // FIXME: callers can't be expected to handle a python module
285 // that has spontaneously broken, but Mgr() should provide
286 // a hook to unload misbehaving modules when they have an
287 // error somewhere like this
291 int MgrPyModule::load_commands()
293 // Don't need a Gil here -- this is called from MgrPyModule::load(),
294 // which already has one.
295 PyObject
*command_list
= PyObject_GetAttrString(pClassInstance
, "COMMANDS");
296 assert(command_list
!= nullptr);
297 const size_t list_size
= PyList_Size(command_list
);
298 for (size_t i
= 0; i
< list_size
; ++i
) {
299 PyObject
*command
= PyList_GetItem(command_list
, i
);
300 assert(command
!= nullptr);
304 PyObject
*pCmd
= PyDict_GetItemString(command
, "cmd");
305 assert(pCmd
!= nullptr);
306 item
.cmdstring
= PyString_AsString(pCmd
);
308 dout(20) << "loaded command " << item
.cmdstring
<< dendl
;
310 PyObject
*pDesc
= PyDict_GetItemString(command
, "desc");
311 assert(pDesc
!= nullptr);
312 item
.helpstring
= PyString_AsString(pDesc
);
314 PyObject
*pPerm
= PyDict_GetItemString(command
, "perm");
315 assert(pPerm
!= nullptr);
316 item
.perm
= PyString_AsString(pPerm
);
320 commands
.push_back(item
);
322 Py_DECREF(command_list
);
324 dout(10) << "loaded " << commands
.size() << " commands" << dendl
;
329 int MgrPyModule::handle_command(
330 const cmdmap_t
&cmdmap
,
331 std::stringstream
*ds
,
332 std::stringstream
*ss
)
334 assert(ss
!= nullptr);
335 assert(ds
!= nullptr);
337 Gil
gil(pMyThreadState
);
340 cmdmap_dump(cmdmap
, &f
);
341 PyObject
*py_cmd
= f
.get();
343 auto pResult
= PyObject_CallMethod(pClassInstance
,
344 const_cast<char*>("handle_command"), const_cast<char*>("(O)"), py_cmd
);
349 if (pResult
!= NULL
) {
350 if (PyTuple_Size(pResult
) != 3) {
353 r
= PyInt_AsLong(PyTuple_GetItem(pResult
, 0));
354 *ds
<< PyString_AsString(PyTuple_GetItem(pResult
, 1));
355 *ss
<< PyString_AsString(PyTuple_GetItem(pResult
, 2));
361 *ss
<< handle_pyerror();