]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/ActivePyModule.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / mgr / ActivePyModule.cc
CommitLineData
3efd9988
FG
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
3efd9988
FG
14#include "PyFormatter.h"
15
16#include "common/debug.h"
17
18#include "ActivePyModule.h"
19
3efd9988
FG
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
3efd9988
FG
26int ActivePyModule::load(ActivePyModules *py_modules)
27{
11fdf7f2
TL
28 ceph_assert(py_modules);
29 Gil gil(py_module->pMyThreadState, true);
3efd9988
FG
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);
11fdf7f2 35 auto pModuleName = PyString_FromString(get_name().c_str());
3efd9988
FG
36 auto pArgs = PyTuple_Pack(3, pModuleName, pPyModules, pThisPtr);
37
11fdf7f2 38 pClassInstance = PyObject_CallObject(py_module->pClass, pArgs);
3efd9988
FG
39 Py_DECREF(pModuleName);
40 Py_DECREF(pArgs);
41 if (pClassInstance == nullptr) {
11fdf7f2 42 derr << "Failed to construct class in '" << get_name() << "'" << dendl;
3efd9988
FG
43 derr << handle_pyerror() << dendl;
44 return -EINVAL;
45 } else {
11fdf7f2 46 dout(1) << "Constructed class from module: " << get_name() << dendl;
3efd9988
FG
47 }
48
11fdf7f2 49 return 0;
3efd9988
FG
50}
51
52void ActivePyModule::notify(const std::string &notify_type, const std::string &notify_id)
53{
11fdf7f2 54 ceph_assert(pClassInstance != nullptr);
3efd9988 55
11fdf7f2 56 Gil gil(py_module->pMyThreadState, true);
3efd9988
FG
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 {
11fdf7f2 66 derr << get_name() << ".notify:" << dendl;
3efd9988
FG
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
75void ActivePyModule::notify_clog(const LogEntry &log_entry)
76{
11fdf7f2 77 ceph_assert(pClassInstance != nullptr);
3efd9988 78
11fdf7f2 79 Gil gil(py_module->pMyThreadState, true);
3efd9988
FG
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 {
11fdf7f2 94 derr << get_name() << ".notify_clog:" << dendl;
3efd9988
FG
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
11fdf7f2 103bool ActivePyModule::method_exists(const std::string &method) const
3efd9988 104{
11fdf7f2
TL
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;
3efd9988 113 }
11fdf7f2
TL
114}
115
116PyObject *ActivePyModule::dispatch_remote(
117 const std::string &method,
118 PyObject *args,
119 PyObject *kwargs,
120 std::string *err)
121{
122 ceph_assert(err != nullptr);
3efd9988 123
11fdf7f2
TL
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
3efd9988 130
11fdf7f2 131 Gil gil(py_module->pMyThreadState, true);
3efd9988 132
11fdf7f2
TL
133 // Fire the receiving method
134 auto boundMethod = PyObject_GetAttrString(pClassInstance, method.c_str());
3efd9988 135
11fdf7f2
TL
136 // Caller should have done method_exists check first!
137 ceph_assert(boundMethod != nullptr);
3efd9988 138
11fdf7f2
TL
139 dout(20) << "Calling " << py_module->get_name()
140 << "." << method << "..." << dendl;
3efd9988 141
11fdf7f2
TL
142 auto remoteResult = PyObject_Call(boundMethod,
143 args, kwargs);
144 Py_DECREF(boundMethod);
3efd9988 145
11fdf7f2
TL
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;
3efd9988 153 }
3efd9988 154
11fdf7f2
TL
155 return remoteResult;
156}
3efd9988 157
11fdf7f2
TL
158void 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 }
3efd9988
FG
169}
170
171int ActivePyModule::handle_command(
172 const cmdmap_t &cmdmap,
11fdf7f2 173 const bufferlist &inbuf,
3efd9988
FG
174 std::stringstream *ds,
175 std::stringstream *ss)
176{
11fdf7f2
TL
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 }
3efd9988 186
11fdf7f2 187 Gil gil(py_module->pMyThreadState, true);
3efd9988
FG
188
189 PyFormatter f;
190 cmdmap_dump(cmdmap, &f);
191 PyObject *py_cmd = f.get();
11fdf7f2
TL
192 string instr;
193 inbuf.copy(0, inbuf.length(), instr);
3efd9988
FG
194
195 auto pResult = PyObject_CallMethod(pClassInstance,
11fdf7f2
TL
196 const_cast<char*>("_handle_command"), const_cast<char*>("s#O"),
197 instr.c_str(), instr.length(), py_cmd);
3efd9988
FG
198
199 Py_DECREF(py_cmd);
200
201 int r = 0;
202 if (pResult != NULL) {
203 if (PyTuple_Size(pResult) != 3) {
11fdf7f2
TL
204 derr << "module '" << py_module->get_name() << "' command handler "
205 "returned wrong type!" << dendl;
3efd9988
FG
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 {
11fdf7f2
TL
215 derr << "module '" << py_module->get_name() << "' command handler "
216 "threw exception: " << peek_pyerror() << dendl;
3efd9988
FG
217 *ds << "";
218 *ss << handle_pyerror();
219 r = -EINVAL;
220 }
221
222 return r;
223}
224
225void ActivePyModule::get_health_checks(health_check_map_t *checks)
226{
227 checks->merge(health_checks);
228}
229