]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/StandbyPyModules.cc
update sources to v12.2.3
[ceph.git] / ceph / src / mgr / StandbyPyModules.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
14#include "StandbyPyModules.h"
15
16#include "common/debug.h"
17
18#include "mgr/MgrContext.h"
19#include "mgr/Gil.h"
20
21
22#include <boost/python.hpp>
23#include "include/assert.h" // boost clobbers this
24
25// For ::config_prefix
26#include "PyModuleRegistry.h"
27
28#define dout_context g_ceph_context
29#define dout_subsys ceph_subsys_mgr
30#undef dout_prefix
31#define dout_prefix *_dout << "mgr " << __func__ << " "
32
33// Declaration fulfilled by ActivePyModules
34std::string handle_pyerror();
35
36
b32b8144
FG
37StandbyPyModules::StandbyPyModules(MonClient *monc_, const MgrMap &mgr_map_,
38 LogChannelRef clog_)
39 : monc(monc_), load_config_thread(monc, &state), clog(clog_)
3efd9988
FG
40{
41 state.set_mgr_map(mgr_map_);
42}
43
44// FIXME: completely identical to ActivePyModules
45void StandbyPyModules::shutdown()
46{
47 Mutex::Locker locker(lock);
48
49 if (!state.is_config_loaded && load_config_thread.is_started()) {
50 // FIXME: handle cases where initial load races with shutdown
51 // this is actually not super rare because
52 assert(0);
53 //load_config_thread.kill(SIGKILL);
54 }
55
56 // Signal modules to drop out of serve() and/or tear down resources
57 for (auto &i : modules) {
58 auto module = i.second.get();
59 const auto& name = i.first;
60 dout(10) << "waiting for module " << name << " to shutdown" << dendl;
61 lock.Unlock();
62 module->shutdown();
63 lock.Lock();
64 dout(10) << "module " << name << " shutdown" << dendl;
65 }
66
67 // For modules implementing serve(), finish the threads where we
68 // were running that.
69 for (auto &i : modules) {
70 lock.Unlock();
71 dout(10) << "joining thread for module " << i.first << dendl;
72 i.second->thread.join();
73 dout(10) << "joined thread for module " << i.first << dendl;
74 lock.Lock();
75 }
76
77 modules.clear();
78}
79
80int StandbyPyModules::start_one(std::string const &module_name,
81 PyObject *pClass, const SafeThreadState &pMyThreadState)
82{
83 Mutex::Locker l(lock);
84
85 assert(modules.count(module_name) == 0);
86
87 modules[module_name].reset(new StandbyPyModule(
88 state,
89 module_name, pClass,
b32b8144 90 pMyThreadState, clog));
3efd9988
FG
91
92 if (modules.size() == 1) {
93 load_config_thread.create("LoadConfig");
94 }
95
96 int r = modules[module_name]->load();
97 if (r != 0) {
98 modules.erase(module_name);
99 return r;
100 } else {
101 dout(4) << "Starting thread for " << module_name << dendl;
102 // Giving Thread the module's module_name member as its
103 // char* thread name: thread must not outlive module class lifetime.
104 modules[module_name]->thread.create(
105 modules[module_name]->get_name().c_str());
106 return 0;
107 }
108}
109
110int StandbyPyModule::load()
111{
112 Gil gil(pMyThreadState, true);
113
114 // We tell the module how we name it, so that it can be consistent
115 // with us in logging etc.
116 auto pThisPtr = PyCapsule_New(this, nullptr, nullptr);
117 assert(pThisPtr != nullptr);
118 auto pModuleName = PyString_FromString(module_name.c_str());
119 assert(pModuleName != nullptr);
120 auto pArgs = PyTuple_Pack(2, pModuleName, pThisPtr);
121 Py_DECREF(pThisPtr);
122 Py_DECREF(pModuleName);
123
124 pClassInstance = PyObject_CallObject(pClass, pArgs);
125 Py_DECREF(pArgs);
126 if (pClassInstance == nullptr) {
127 derr << "Failed to construct class in '" << module_name << "'" << dendl;
128 derr << handle_pyerror() << dendl;
129 return -EINVAL;
130 } else {
131 dout(1) << "Constructed class from module: " << module_name << dendl;
132 return 0;
133 }
134}
135
136void *StandbyPyModules::LoadConfigThread::entry()
137{
138 dout(10) << "listing keys" << dendl;
139 JSONCommand cmd;
140 cmd.run(monc, "{\"prefix\": \"config-key ls\"}");
141 cmd.wait();
142 assert(cmd.r == 0);
143
144 std::map<std::string, std::string> loaded;
145
146 for (auto &key_str : cmd.json_result.get_array()) {
147 std::string const key = key_str.get_str();
148 dout(20) << "saw key '" << key << "'" << dendl;
149
150 const std::string config_prefix = PyModuleRegistry::config_prefix;
151
152 if (key.substr(0, config_prefix.size()) == config_prefix) {
153 dout(20) << "fetching '" << key << "'" << dendl;
154 Command get_cmd;
155 std::ostringstream cmd_json;
156 cmd_json << "{\"prefix\": \"config-key get\", \"key\": \"" << key << "\"}";
157 get_cmd.run(monc, cmd_json.str());
158 get_cmd.wait();
159 assert(get_cmd.r == 0);
160 loaded[key] = get_cmd.outbl.to_str();
161 }
162 }
163 state->loaded_config(loaded);
164
165 return nullptr;
166}
167
168bool StandbyPyModule::get_config(const std::string &key,
169 std::string *value) const
170{
171 PyThreadState *tstate = PyEval_SaveThread();
172 PyEval_RestoreThread(tstate);
173
174 const std::string global_key = PyModuleRegistry::config_prefix
175 + module_name + "/" + key;
176
177 dout(4) << __func__ << "key: " << global_key << dendl;
178
179 return state.with_config([global_key, value](const PyModuleConfig &config){
180 if (config.count(global_key)) {
181 *value = config.at(global_key);
182 return true;
183 } else {
184 return false;
185 }
186 });
187}
188
189std::string StandbyPyModule::get_active_uri() const
190{
191 std::string result;
192 state.with_mgr_map([&result, this](const MgrMap &mgr_map){
193 auto iter = mgr_map.services.find(module_name);
194 if (iter != mgr_map.services.end()) {
195 result = iter->second;
196 }
197 });
198
199 return result;
200}
201