]>
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.
15 #include "PyFormatter.h"
17 #include "common/debug.h"
19 #include "MgrPyModule.h"
21 //XXX courtesy of http://stackoverflow.com/questions/1418015/how-to-get-python-exception-text
22 #include <boost/python.hpp>
23 #include "include/assert.h" // boost clobbers this
25 // decode a Python exception into a string
26 std::string
handle_pyerror()
28 using namespace boost::python
;
29 using namespace boost
;
31 PyObject
*exc
, *val
, *tb
;
32 object formatted_list
, formatted
;
33 PyErr_Fetch(&exc
, &val
, &tb
);
34 handle
<> hexc(exc
), hval(allow_null(val
)), htb(allow_null(tb
));
35 object
traceback(import("traceback"));
37 object
format_exception_only(traceback
.attr("format_exception_only"));
38 formatted_list
= format_exception_only(hexc
, hval
);
40 object
format_exception(traceback
.attr("format_exception"));
41 formatted_list
= format_exception(hexc
,hval
, htb
);
43 formatted
= str("").join(formatted_list
);
44 return extract
<std::string
>(formatted
);
47 #define dout_context g_ceph_context
48 #define dout_subsys ceph_subsys_mgr
50 #define dout_prefix *_dout << "mgr " << __func__ << " "
52 MgrPyModule::MgrPyModule(const std::string
&module_name_
)
53 : module_name(module_name_
),
54 pClassInstance(nullptr)
57 MgrPyModule::~MgrPyModule()
59 PyGILState_STATE gstate
;
60 gstate
= PyGILState_Ensure();
62 Py_XDECREF(pClassInstance
);
64 PyGILState_Release(gstate
);
67 int MgrPyModule::load()
70 PyObject
*pName
= PyString_FromString(module_name
.c_str());
71 auto pModule
= PyImport_Import(pName
);
73 if (pModule
== nullptr) {
74 derr
<< "Module not found: '" << module_name
<< "'" << dendl
;
79 // TODO: let them call it what they want instead of just 'Module'
80 auto pClass
= PyObject_GetAttrString(pModule
, (const char*)"Module");
82 if (pClass
== nullptr) {
83 derr
<< "Class not found in module '" << module_name
<< "'" << dendl
;
88 // Just using the module name as the handle, replace with a
89 // uuidish thing if needed
90 auto pyHandle
= PyString_FromString(module_name
.c_str());
91 auto pArgs
= PyTuple_Pack(1, pyHandle
);
92 pClassInstance
= PyObject_CallObject(pClass
, pArgs
);
96 if (pClassInstance
== nullptr) {
97 derr
<< "Failed to construct class in '" << module_name
<< "'" << dendl
;
100 dout(1) << "Constructed class from module: " << module_name
<< dendl
;
103 return load_commands();
106 int MgrPyModule::serve()
108 assert(pClassInstance
!= nullptr);
110 PyGILState_STATE gstate
;
111 gstate
= PyGILState_Ensure();
113 auto pValue
= PyObject_CallMethod(pClassInstance
,
114 const_cast<char*>("serve"), nullptr);
117 if (pValue
!= NULL
) {
120 derr
<< module_name
<< ".serve:" << dendl
;
121 derr
<< handle_pyerror() << dendl
;
125 PyGILState_Release(gstate
);
130 // FIXME: DRY wrt serve
131 void MgrPyModule::shutdown()
133 assert(pClassInstance
!= nullptr);
135 PyGILState_STATE gstate
;
136 gstate
= PyGILState_Ensure();
138 auto pValue
= PyObject_CallMethod(pClassInstance
,
139 const_cast<char*>("shutdown"), nullptr);
141 if (pValue
!= NULL
) {
144 derr
<< "Failed to invoke shutdown() on " << module_name
<< dendl
;
145 derr
<< handle_pyerror() << dendl
;
148 PyGILState_Release(gstate
);
151 void MgrPyModule::notify(const std::string
¬ify_type
, const std::string
¬ify_id
)
153 assert(pClassInstance
!= nullptr);
155 PyGILState_STATE gstate
;
156 gstate
= PyGILState_Ensure();
159 auto pValue
= PyObject_CallMethod(pClassInstance
,
160 const_cast<char*>("notify"), const_cast<char*>("(ss)"),
161 notify_type
.c_str(), notify_id
.c_str());
163 if (pValue
!= NULL
) {
166 derr
<< module_name
<< ".notify:" << dendl
;
167 derr
<< handle_pyerror() << dendl
;
168 // FIXME: callers can't be expected to handle a python module
169 // that has spontaneously broken, but Mgr() should provide
170 // a hook to unload misbehaving modules when they have an
171 // error somewhere like this
174 PyGILState_Release(gstate
);
177 void MgrPyModule::notify_clog(const LogEntry
&log_entry
)
179 assert(pClassInstance
!= nullptr);
181 PyGILState_STATE gstate
;
182 gstate
= PyGILState_Ensure();
184 // Construct python-ized LogEntry
187 auto py_log_entry
= f
.get();
190 auto pValue
= PyObject_CallMethod(pClassInstance
,
191 const_cast<char*>("notify"), const_cast<char*>("(sN)"),
192 "clog", py_log_entry
);
194 if (pValue
!= NULL
) {
197 derr
<< module_name
<< ".notify_clog:" << dendl
;
198 derr
<< handle_pyerror() << dendl
;
199 // FIXME: callers can't be expected to handle a python module
200 // that has spontaneously broken, but Mgr() should provide
201 // a hook to unload misbehaving modules when they have an
202 // error somewhere like this
205 PyGILState_Release(gstate
);
208 int MgrPyModule::load_commands()
210 PyGILState_STATE gstate
;
211 gstate
= PyGILState_Ensure();
213 PyObject
*command_list
= PyObject_GetAttrString(pClassInstance
, "COMMANDS");
214 assert(command_list
!= nullptr);
215 const size_t list_size
= PyList_Size(command_list
);
216 for (size_t i
= 0; i
< list_size
; ++i
) {
217 PyObject
*command
= PyList_GetItem(command_list
, i
);
218 assert(command
!= nullptr);
222 PyObject
*pCmd
= PyDict_GetItemString(command
, "cmd");
223 assert(pCmd
!= nullptr);
224 item
.cmdstring
= PyString_AsString(pCmd
);
226 dout(20) << "loaded command " << item
.cmdstring
<< dendl
;
228 PyObject
*pDesc
= PyDict_GetItemString(command
, "desc");
229 assert(pDesc
!= nullptr);
230 item
.helpstring
= PyString_AsString(pDesc
);
232 PyObject
*pPerm
= PyDict_GetItemString(command
, "perm");
233 assert(pPerm
!= nullptr);
234 item
.perm
= PyString_AsString(pPerm
);
238 commands
.push_back(item
);
240 Py_DECREF(command_list
);
242 PyGILState_Release(gstate
);
244 dout(10) << "loaded " << commands
.size() << " commands" << dendl
;
249 int MgrPyModule::handle_command(
250 const cmdmap_t
&cmdmap
,
251 std::stringstream
*ds
,
252 std::stringstream
*ss
)
254 assert(ss
!= nullptr);
255 assert(ds
!= nullptr);
257 PyGILState_STATE gstate
;
258 gstate
= PyGILState_Ensure();
261 cmdmap_dump(cmdmap
, &f
);
262 PyObject
*py_cmd
= f
.get();
264 auto pResult
= PyObject_CallMethod(pClassInstance
,
265 const_cast<char*>("handle_command"), const_cast<char*>("(O)"), py_cmd
);
270 if (pResult
!= NULL
) {
271 if (PyTuple_Size(pResult
) != 3) {
274 r
= PyInt_AsLong(PyTuple_GetItem(pResult
, 0));
275 *ds
<< PyString_AsString(PyTuple_GetItem(pResult
, 1));
276 *ss
<< PyString_AsString(PyTuple_GetItem(pResult
, 2));
282 *ss
<< handle_pyerror();
286 PyGILState_Release(gstate
);