]>
Commit | Line | Data |
---|---|---|
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 |
26 | int 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 | ||
52 | void ActivePyModule::notify(const std::string ¬ify_type, const std::string ¬ify_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 | ||
75 | void 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 | 103 | bool 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 | ||
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); | |
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 |
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 | } | |
3efd9988 FG |
169 | } |
170 | ||
171 | int 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 | ||
225 | void ActivePyModule::get_health_checks(health_check_map_t *checks) | |
226 | { | |
227 | checks->merge(health_checks); | |
228 | } | |
229 |