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