]>
git.proxmox.com Git - ceph.git/blob - ceph/src/mgr/ActivePyModule.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.
14 #include "PyFormatter.h"
16 #include "common/debug.h"
18 #include "ActivePyModule.h"
21 #define dout_context g_ceph_context
22 #define dout_subsys ceph_subsys_mgr
24 #define dout_prefix *_dout << "mgr " << __func__ << " "
26 int ActivePyModule::load(ActivePyModules
*py_modules
)
28 ceph_assert(py_modules
);
29 Gil
gil(py_module
->pMyThreadState
, true);
31 // We tell the module how we name it, so that it can be consistent
32 // with us in logging etc.
33 auto pThisPtr
= PyCapsule_New(this, nullptr, nullptr);
34 auto pPyModules
= PyCapsule_New(py_modules
, nullptr, nullptr);
35 auto pModuleName
= PyString_FromString(get_name().c_str());
36 auto pArgs
= PyTuple_Pack(3, pModuleName
, pPyModules
, pThisPtr
);
38 pClassInstance
= PyObject_CallObject(py_module
->pClass
, pArgs
);
39 Py_DECREF(pModuleName
);
41 if (pClassInstance
== nullptr) {
42 derr
<< "Failed to construct class in '" << get_name() << "'" << dendl
;
43 derr
<< handle_pyerror() << dendl
;
46 dout(1) << "Constructed class from module: " << get_name() << dendl
;
52 void ActivePyModule::notify(const std::string
¬ify_type
, const std::string
¬ify_id
)
54 ceph_assert(pClassInstance
!= nullptr);
56 Gil
gil(py_module
->pMyThreadState
, true);
59 auto pValue
= PyObject_CallMethod(pClassInstance
,
60 const_cast<char*>("notify"), const_cast<char*>("(ss)"),
61 notify_type
.c_str(), notify_id
.c_str());
66 derr
<< get_name() << ".notify:" << dendl
;
67 derr
<< handle_pyerror() << dendl
;
68 // FIXME: callers can't be expected to handle a python module
69 // that has spontaneously broken, but Mgr() should provide
70 // a hook to unload misbehaving modules when they have an
71 // error somewhere like this
75 void ActivePyModule::notify_clog(const LogEntry
&log_entry
)
77 ceph_assert(pClassInstance
!= nullptr);
79 Gil
gil(py_module
->pMyThreadState
, true);
81 // Construct python-ized LogEntry
84 auto py_log_entry
= f
.get();
87 auto pValue
= PyObject_CallMethod(pClassInstance
,
88 const_cast<char*>("notify"), const_cast<char*>("(sN)"),
89 "clog", py_log_entry
);
94 derr
<< get_name() << ".notify_clog:" << dendl
;
95 derr
<< handle_pyerror() << dendl
;
96 // FIXME: callers can't be expected to handle a python module
97 // that has spontaneously broken, but Mgr() should provide
98 // a hook to unload misbehaving modules when they have an
99 // error somewhere like this
103 bool ActivePyModule::method_exists(const std::string
&method
) const
105 Gil
gil(py_module
->pMyThreadState
, true);
107 auto boundMethod
= PyObject_GetAttrString(pClassInstance
, method
.c_str());
108 if (boundMethod
== nullptr) {
111 Py_DECREF(boundMethod
);
116 PyObject
*ActivePyModule::dispatch_remote(
117 const std::string
&method
,
122 ceph_assert(err
!= nullptr);
124 // Rather than serializing arguments, pass the CPython objects.
125 // Works because we happen to know that the subinterpreter
126 // implementation shares a GIL, allocator, deallocator and GC state, so
127 // it's okay to pass the objects between subinterpreters.
128 // But in future this might involve serialization to support a CSP-aware
129 // future Python interpreter a la PEP554
131 Gil
gil(py_module
->pMyThreadState
, true);
133 // Fire the receiving method
134 auto boundMethod
= PyObject_GetAttrString(pClassInstance
, method
.c_str());
136 // Caller should have done method_exists check first!
137 ceph_assert(boundMethod
!= nullptr);
139 dout(20) << "Calling " << py_module
->get_name()
140 << "." << method
<< "..." << dendl
;
142 auto remoteResult
= PyObject_Call(boundMethod
,
144 Py_DECREF(boundMethod
);
146 if (remoteResult
== nullptr) {
147 // Because the caller is in a different context, we can't let this
148 // exception bubble up, need to re-raise it from the caller's
150 *err
= handle_pyerror();
152 dout(20) << "Success calling '" << method
<< "'" << dendl
;
158 void ActivePyModule::config_notify()
160 Gil
gil(py_module
->pMyThreadState
, true);
161 dout(20) << "Calling " << py_module
->get_name() << ".config_notify..."
163 auto remoteResult
= PyObject_CallMethod(pClassInstance
,
164 const_cast<char*>("config_notify"),
166 if (remoteResult
!= nullptr) {
167 Py_DECREF(remoteResult
);
171 int ActivePyModule::handle_command(
172 const cmdmap_t
&cmdmap
,
173 const bufferlist
&inbuf
,
174 std::stringstream
*ds
,
175 std::stringstream
*ss
)
177 ceph_assert(ss
!= nullptr);
178 ceph_assert(ds
!= nullptr);
180 if (pClassInstance
== nullptr) {
181 // Not the friendliest error string, but we could only
182 // hit this in quite niche cases, if at all.
183 *ss
<< "Module not instantiated";
187 Gil
gil(py_module
->pMyThreadState
, true);
190 cmdmap_dump(cmdmap
, &f
);
191 PyObject
*py_cmd
= f
.get();
193 inbuf
.copy(0, inbuf
.length(), instr
);
195 auto pResult
= PyObject_CallMethod(pClassInstance
,
196 const_cast<char*>("_handle_command"), const_cast<char*>("s#O"),
197 instr
.c_str(), instr
.length(), py_cmd
);
202 if (pResult
!= NULL
) {
203 if (PyTuple_Size(pResult
) != 3) {
204 derr
<< "module '" << py_module
->get_name() << "' command handler "
205 "returned wrong type!" << dendl
;
208 r
= PyInt_AsLong(PyTuple_GetItem(pResult
, 0));
209 *ds
<< PyString_AsString(PyTuple_GetItem(pResult
, 1));
210 *ss
<< PyString_AsString(PyTuple_GetItem(pResult
, 2));
215 derr
<< "module '" << py_module
->get_name() << "' command handler "
216 "threw exception: " << peek_pyerror() << dendl
;
218 *ss
<< handle_pyerror();
225 void ActivePyModule::get_health_checks(health_check_map_t
*checks
)
227 checks
->merge(health_checks
);