]> git.proxmox.com Git - ceph.git/blob - ceph/src/mgr/ActivePyModule.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / 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
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2016 John Spray <john.spray@redhat.com>
7 *
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.
12 */
13
14 #include "PyFormatter.h"
15
16 #include "common/debug.h"
17
18 #include "ActivePyModule.h"
19
20
21 #define dout_context g_ceph_context
22 #define dout_subsys ceph_subsys_mgr
23 #undef dout_prefix
24 #define dout_prefix *_dout << "mgr " << __func__ << " "
25
26 int ActivePyModule::load(ActivePyModules *py_modules)
27 {
28 ceph_assert(py_modules);
29 Gil gil(py_module->pMyThreadState, true);
30
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);
37
38 pClassInstance = PyObject_CallObject(py_module->pClass, pArgs);
39 Py_DECREF(pModuleName);
40 Py_DECREF(pArgs);
41 if (pClassInstance == nullptr) {
42 derr << "Failed to construct class in '" << get_name() << "'" << dendl;
43 derr << handle_pyerror() << dendl;
44 return -EINVAL;
45 } else {
46 dout(1) << "Constructed class from module: " << get_name() << dendl;
47 }
48
49 return 0;
50 }
51
52 void ActivePyModule::notify(const std::string &notify_type, const std::string &notify_id)
53 {
54 ceph_assert(pClassInstance != nullptr);
55
56 Gil gil(py_module->pMyThreadState, true);
57
58 // Execute
59 auto pValue = PyObject_CallMethod(pClassInstance,
60 const_cast<char*>("notify"), const_cast<char*>("(ss)"),
61 notify_type.c_str(), notify_id.c_str());
62
63 if (pValue != NULL) {
64 Py_DECREF(pValue);
65 } else {
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
72 }
73 }
74
75 void ActivePyModule::notify_clog(const LogEntry &log_entry)
76 {
77 ceph_assert(pClassInstance != nullptr);
78
79 Gil gil(py_module->pMyThreadState, true);
80
81 // Construct python-ized LogEntry
82 PyFormatter f;
83 log_entry.dump(&f);
84 auto py_log_entry = f.get();
85
86 // Execute
87 auto pValue = PyObject_CallMethod(pClassInstance,
88 const_cast<char*>("notify"), const_cast<char*>("(sN)"),
89 "clog", py_log_entry);
90
91 if (pValue != NULL) {
92 Py_DECREF(pValue);
93 } else {
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
100 }
101 }
102
103 bool ActivePyModule::method_exists(const std::string &method) const
104 {
105 Gil gil(py_module->pMyThreadState, true);
106
107 auto boundMethod = PyObject_GetAttrString(pClassInstance, method.c_str());
108 if (boundMethod == nullptr) {
109 return false;
110 } else {
111 Py_DECREF(boundMethod);
112 return true;
113 }
114 }
115
116 PyObject *ActivePyModule::dispatch_remote(
117 const std::string &method,
118 PyObject *args,
119 PyObject *kwargs,
120 std::string *err)
121 {
122 ceph_assert(err != nullptr);
123
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
130
131 Gil gil(py_module->pMyThreadState, true);
132
133 // Fire the receiving method
134 auto boundMethod = PyObject_GetAttrString(pClassInstance, method.c_str());
135
136 // Caller should have done method_exists check first!
137 ceph_assert(boundMethod != nullptr);
138
139 dout(20) << "Calling " << py_module->get_name()
140 << "." << method << "..." << dendl;
141
142 auto remoteResult = PyObject_Call(boundMethod,
143 args, kwargs);
144 Py_DECREF(boundMethod);
145
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
149 // context later.
150 *err = handle_pyerror();
151 } else {
152 dout(20) << "Success calling '" << method << "'" << dendl;
153 }
154
155 return remoteResult;
156 }
157
158 void ActivePyModule::config_notify()
159 {
160 Gil gil(py_module->pMyThreadState, true);
161 dout(20) << "Calling " << py_module->get_name() << ".config_notify..."
162 << dendl;
163 auto remoteResult = PyObject_CallMethod(pClassInstance,
164 const_cast<char*>("config_notify"),
165 (char*)NULL);
166 if (remoteResult != nullptr) {
167 Py_DECREF(remoteResult);
168 }
169 }
170
171 int ActivePyModule::handle_command(
172 const cmdmap_t &cmdmap,
173 const bufferlist &inbuf,
174 std::stringstream *ds,
175 std::stringstream *ss)
176 {
177 ceph_assert(ss != nullptr);
178 ceph_assert(ds != nullptr);
179
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";
184 return -EINVAL;
185 }
186
187 Gil gil(py_module->pMyThreadState, true);
188
189 PyFormatter f;
190 cmdmap_dump(cmdmap, &f);
191 PyObject *py_cmd = f.get();
192 string instr;
193 inbuf.copy(0, inbuf.length(), instr);
194
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);
198
199 Py_DECREF(py_cmd);
200
201 int r = 0;
202 if (pResult != NULL) {
203 if (PyTuple_Size(pResult) != 3) {
204 derr << "module '" << py_module->get_name() << "' command handler "
205 "returned wrong type!" << dendl;
206 r = -EINVAL;
207 } else {
208 r = PyInt_AsLong(PyTuple_GetItem(pResult, 0));
209 *ds << PyString_AsString(PyTuple_GetItem(pResult, 1));
210 *ss << PyString_AsString(PyTuple_GetItem(pResult, 2));
211 }
212
213 Py_DECREF(pResult);
214 } else {
215 derr << "module '" << py_module->get_name() << "' command handler "
216 "threw exception: " << peek_pyerror() << dendl;
217 *ds << "";
218 *ss << handle_pyerror();
219 r = -EINVAL;
220 }
221
222 return r;
223 }
224
225 void ActivePyModule::get_health_checks(health_check_map_t *checks)
226 {
227 checks->merge(health_checks);
228 }
229