]> git.proxmox.com Git - ceph.git/blame - ceph/src/mgr/StandbyPyModules.cc
import 15.2.0 Octopus source
[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
11fdf7f2 16#include "common/Finisher.h"
3efd9988 17#include "common/debug.h"
11fdf7f2 18#include "common/errno.h"
3efd9988
FG
19
20#include "mgr/MgrContext.h"
21#include "mgr/Gil.h"
22
23
24#include <boost/python.hpp>
11fdf7f2 25#include "include/ceph_assert.h" // boost clobbers this
3efd9988
FG
26
27// For ::config_prefix
28#include "PyModuleRegistry.h"
29
30#define dout_context g_ceph_context
31#define dout_subsys ceph_subsys_mgr
32#undef dout_prefix
33#define dout_prefix *_dout << "mgr " << __func__ << " "
34
3efd9988 35
11fdf7f2
TL
36StandbyPyModules::StandbyPyModules(
37 const MgrMap &mgr_map_,
38 PyModuleConfig &module_config,
39 LogChannelRef clog_,
40 MonClient &monc_,
41 Finisher &f)
42 : state(module_config, monc_),
43 clog(clog_),
44 finisher(f)
3efd9988
FG
45{
46 state.set_mgr_map(mgr_map_);
47}
48
49// FIXME: completely identical to ActivePyModules
50void StandbyPyModules::shutdown()
51{
11fdf7f2 52 std::lock_guard locker(lock);
3efd9988
FG
53
54 // Signal modules to drop out of serve() and/or tear down resources
55 for (auto &i : modules) {
56 auto module = i.second.get();
57 const auto& name = i.first;
58 dout(10) << "waiting for module " << name << " to shutdown" << dendl;
9f95a23c 59 lock.unlock();
3efd9988 60 module->shutdown();
9f95a23c 61 lock.lock();
3efd9988
FG
62 dout(10) << "module " << name << " shutdown" << dendl;
63 }
64
65 // For modules implementing serve(), finish the threads where we
66 // were running that.
67 for (auto &i : modules) {
9f95a23c 68 lock.unlock();
3efd9988
FG
69 dout(10) << "joining thread for module " << i.first << dendl;
70 i.second->thread.join();
71 dout(10) << "joined thread for module " << i.first << dendl;
9f95a23c 72 lock.lock();
3efd9988
FG
73 }
74
75 modules.clear();
76}
77
11fdf7f2 78void StandbyPyModules::start_one(PyModuleRef py_module)
3efd9988 79{
11fdf7f2
TL
80 std::lock_guard l(lock);
81 const auto name = py_module->get_name();
82
83 ceph_assert(modules.count(name) == 0);
84
85 modules[name].reset(new StandbyPyModule(state, py_module, clog));
86 auto standby_module = modules.at(name).get();
87
88 // Send all python calls down a Finisher to avoid blocking
89 // C++ code, and avoid any potential lock cycles.
9f95a23c 90 finisher.queue(new LambdaContext([this, standby_module, name](int) {
11fdf7f2
TL
91 int r = standby_module->load();
92 if (r != 0) {
93 derr << "Failed to run module in standby mode ('" << name << "')"
94 << dendl;
95 std::lock_guard l(lock);
96 modules.erase(name);
97 } else {
98 dout(4) << "Starting thread for " << name << dendl;
99 standby_module->thread.create(standby_module->get_thread_name());
100 }
101 }));
3efd9988
FG
102}
103
104int StandbyPyModule::load()
105{
11fdf7f2 106 Gil gil(py_module->pMyThreadState, true);
3efd9988
FG
107
108 // We tell the module how we name it, so that it can be consistent
109 // with us in logging etc.
110 auto pThisPtr = PyCapsule_New(this, nullptr, nullptr);
11fdf7f2 111 ceph_assert(pThisPtr != nullptr);
9f95a23c 112 auto pModuleName = PyUnicode_FromString(get_name().c_str());
11fdf7f2 113 ceph_assert(pModuleName != nullptr);
3efd9988
FG
114 auto pArgs = PyTuple_Pack(2, pModuleName, pThisPtr);
115 Py_DECREF(pThisPtr);
116 Py_DECREF(pModuleName);
117
11fdf7f2 118 pClassInstance = PyObject_CallObject(py_module->pStandbyClass, pArgs);
3efd9988
FG
119 Py_DECREF(pArgs);
120 if (pClassInstance == nullptr) {
11fdf7f2 121 derr << "Failed to construct class in '" << get_name() << "'" << dendl;
3efd9988
FG
122 derr << handle_pyerror() << dendl;
123 return -EINVAL;
124 } else {
11fdf7f2 125 dout(1) << "Constructed class from module: " << get_name() << dendl;
3efd9988
FG
126 return 0;
127 }
128}
129
3efd9988
FG
130bool StandbyPyModule::get_config(const std::string &key,
131 std::string *value) const
132{
11fdf7f2
TL
133 const std::string global_key = PyModule::config_prefix
134 + get_name() + "/" + key;
3efd9988 135
11fdf7f2
TL
136 dout(4) << __func__ << " key: " << global_key << dendl;
137
3efd9988 138 return state.with_config([global_key, value](const PyModuleConfig &config){
11fdf7f2
TL
139 if (config.config.count(global_key)) {
140 *value = config.config.at(global_key);
3efd9988
FG
141 return true;
142 } else {
143 return false;
144 }
145 });
146}
147
11fdf7f2
TL
148bool StandbyPyModule::get_store(const std::string &key,
149 std::string *value) const
150{
151
152 const std::string global_key = PyModule::config_prefix
153 + get_name() + "/" + key;
154
155 dout(4) << __func__ << " key: " << global_key << dendl;
156
157 // Active modules use a cache of store values (kept up to date
158 // as writes pass through the active mgr), but standbys
159 // fetch values synchronously to get an up to date value.
160 // It's an acceptable cost because standby modules should not be
161 // doing a lot.
162
163 MonClient &monc = state.get_monc();
164
165 std::ostringstream cmd_json;
166 cmd_json << "{\"prefix\": \"config-key get\", \"key\": \""
167 << global_key << "\"}";
168
169 bufferlist outbl;
170 std::string outs;
171 C_SaferCond c;
172 monc.start_mon_command(
173 {cmd_json.str()},
174 {},
175 &outbl,
176 &outs,
177 &c);
178
179 int r = c.wait();
180 if (r == -ENOENT) {
181 return false;
182 } else if (r != 0) {
183 // This is some internal error, not meaningful to python modules,
184 // so let them just see no value.
185 derr << __func__ << " error fetching store key '" << global_key << "': "
186 << cpp_strerror(r) << " " << outs << dendl;
187 return false;
188 } else {
189 *value = outbl.to_str();
190 return true;
191 }
192}
193
3efd9988
FG
194std::string StandbyPyModule::get_active_uri() const
195{
196 std::string result;
197 state.with_mgr_map([&result, this](const MgrMap &mgr_map){
11fdf7f2 198 auto iter = mgr_map.services.find(get_name());
3efd9988
FG
199 if (iter != mgr_map.services.end()) {
200 result = iter->second;
201 }
202 });
203
204 return result;
205}
206