#include "mon/MonClient.h"
#include "include/stringify.h"
#include "global/global_context.h"
+#include "global/signal_handler.h"
#include "mgr/MgrContext.h"
finisher.start();
+ py_modules.init();
+
dout(4) << "Complete." << dendl;
return 0;
}
py_modules.insert_config(loaded);
}
+void Mgr::handle_signal(int signum)
+{
+ Mutex::Locker l(lock);
+ assert(signum == SIGINT || signum == SIGTERM);
+ shutdown();
+}
void Mgr::shutdown()
{
// to touch references to the things we're about to tear down
finisher.stop();
- lock.Lock();
+ //lock.Lock();
timer.shutdown();
objecter->shutdown();
- lock.Unlock();
+ //lock.Unlock();
monc->shutdown();
client_messenger->shutdown();
- client_messenger->wait();
}
void Mgr::handle_osd_map()
return *authorizer != NULL;
}
+// A reference for use by the signal handler
+Mgr *signal_mgr = nullptr;
+
+static void handle_mgr_signal(int signum)
+{
+ if (signal_mgr) {
+ signal_mgr->handle_signal(signum);
+ }
+}
int Mgr::main(vector<const char *> args)
{
- return py_modules.main(args);
+ py_modules.start();
+
+ // Enable signal handlers
+ signal_mgr = this;
+ init_async_signal_handler();
+ register_async_signal_handler_oneshot(SIGINT, handle_mgr_signal);
+ register_async_signal_handler_oneshot(SIGTERM, handle_mgr_signal);
+
+ client_messenger->wait();
+
+ // Disable signal handlers
+ unregister_async_signal_handler(SIGINT, handle_mgr_signal);
+ unregister_async_signal_handler(SIGTERM, handle_mgr_signal);
+ shutdown_async_signal_handler();
+ signal_mgr = nullptr;
}
*/
+#include <boost/tokenizer.hpp>
+#include "common/errno.h"
+
#include "PyState.h"
#include "PyFormatter.h"
return extract<std::string>(formatted);
}
-int PyModules::main(vector<const char *> args)
+
+int PyModules::init()
{
+ Mutex::Locker locker(lock);
+
global_handle = this;
// Set up global python interpreter
}
// Load python code
- // TODO load mgr_modules list, run them all in a thread each.
- auto mod = new MgrPyModule("rest");
- int r = mod->load();
- if (r != 0) {
- derr << "Error loading python module" << dendl;
- derr << handle_pyerror() << dendl;
-#if 0
- PyObject *ptype, *pvalue, *ptraceback;
- PyErr_Fetch(&ptype, &pvalue, &ptraceback);
- if (ptype) {
- if (pvalue) {
- char *pStrErrorMessage = PyString_AsString(pvalue);
-
-XXX why is pvalue giving null when converted to string?
-
- assert(pStrErrorMessage != nullptr);
- derr << "Exception: " << pStrErrorMessage << dendl;
- Py_DECREF(ptraceback);
- Py_DECREF(pvalue);
- }
- Py_DECREF(ptype);
+ boost::tokenizer<> tok(g_conf->mgr_modules);
+ for(boost::tokenizer<>::iterator module_name=tok.begin();
+ module_name != tok.end();++module_name){
+ dout(1) << "Loading python module '" << *module_name << "'" << dendl;
+ auto mod = new MgrPyModule(*module_name);
+ int r = mod->load();
+ if (r != 0) {
+ derr << "Error loading module '" << *module_name << "': "
+ << cpp_strerror(r) << dendl;
+ derr << handle_pyerror() << dendl;
+
+ return r;
+ } else {
+ // Success!
+ modules[*module_name] = mod;
}
+ }
+
+ // Drop the GIL
+#if 1
+ PyThreadState *tstate = PyEval_SaveThread();
+#else
+ PyGILState_STATE gstate;
+ gstate = PyGILState_Ensure();
+ PyGILState_Release(gstate);
#endif
+
+ return 0;
+}
- // FIXME: be tolerant of bad modules, log an error and continue
- // to load other, healthy modules.
- return r;
- }
+class ServeThread : public Thread
+{
+ MgrPyModule *mod;
+
+public:
+ ServeThread(MgrPyModule *mod_)
+ : mod(mod_) {}
+
+ void *entry()
{
- Mutex::Locker locker(lock);
- modules["rest"] = mod;
- }
+ PyGILState_STATE gstate;
+ gstate = PyGILState_Ensure();
- // Execute python server
- mod->serve();
+ dout(4) << "Entering thread for " << mod->get_name() << dendl;
+ mod->serve();
+
+ PyGILState_Release(gstate);
+
+ return nullptr;
+ }
+};
+void PyModules::start()
+{
{
- Mutex::Locker locker(lock);
- // Tear down modules
- for (auto i : modules) {
- delete i.second;
+ Mutex::Locker l(lock);
+ for (auto &i : modules) {
+ auto thread = new ServeThread(i.second);
+ serve_threads[i.first] = thread;
}
- modules.clear();
}
+ for (auto &i : serve_threads) {
+ std::ostringstream thread_name;
+ thread_name << "mgr." << i.first;
+ dout(4) << "Starting thread for " << i.first << dendl;
+ i.second->create(thread_name.str().c_str());
+ }
+}
+
+void PyModules::shutdown()
+{
+ Mutex::Locker locker(lock);
+
+ // Signal modules to drop out of serve()
+ for (auto i : modules) {
+ auto module = i.second;
+ finisher.queue(new C_StdFunction([module](){
+ module->shutdown();
+ }));
+ }
+
+ for (auto &i : serve_threads) {
+ lock.Unlock();
+ i.second->join();
+ lock.Lock();
+ delete i.second;
+ }
+ serve_threads.clear();
+
+ // Tear down modules
+ for (auto i : modules) {
+ delete i.second;
+ }
+ modules.clear();
+
Py_Finalize();
- return 0;
}
void PyModules::notify_all(const std::string ¬ify_type,
bool PyModules::get_config(const std::string &handle,
const std::string &key, std::string *val) const
{
+ PyThreadState *tstate = PyEval_SaveThread();
+ Mutex::Locker l(lock);
+ PyEval_RestoreThread(tstate);
+
const std::string global_key = config_prefix + handle + "." + key;
if (config_cache.count(global_key)) {
{
const std::string global_key = config_prefix + handle + "." + key;
- config_cache[global_key] = val;
-
- std::ostringstream cmd_json;
Command set_cmd;
-
- JSONFormatter jf;
- jf.open_object_section("cmd");
- jf.dump_string("prefix", "config-key put");
- jf.dump_string("key", global_key);
- jf.dump_string("val", val);
- jf.close_section();
- jf.flush(cmd_json);
-
- set_cmd.run(&monc, cmd_json.str());
+ {
+ PyThreadState *tstate = PyEval_SaveThread();
+ Mutex::Locker l(lock);
+ PyEval_RestoreThread(tstate);
+ config_cache[global_key] = val;
+
+ std::ostringstream cmd_json;
+
+ JSONFormatter jf;
+ jf.open_object_section("cmd");
+ jf.dump_string("prefix", "config-key put");
+ jf.dump_string("key", global_key);
+ jf.dump_string("val", val);
+ jf.close_section();
+ jf.flush(cmd_json);
+
+ set_cmd.run(&monc, cmd_json.str());
+ }
set_cmd.wait();
// FIXME: is config-key put ever allowed to fail?
void PyModules::insert_config(const std::map<std::string,
std::string> &new_config)
{
+ Mutex::Locker l(lock);
+
dout(4) << "Loaded " << new_config.size() << " config settings" << dendl;
config_cache = new_config;
}
#include "common/Finisher.h"
#include "common/Mutex.h"
-
+#include "common/Thread.h"
#include "DaemonState.h"
#include "ClusterState.h"
-
-
-
+class ServeThread;
class PyModules
{
protected:
std::map<std::string, MgrPyModule*> modules;
+ std::map<std::string, ServeThread*> serve_threads;
DaemonStateIndex &daemon_state;
ClusterState &cluster_state;
MonClient &monc;
Finisher &finisher;
- Mutex lock;
+ mutable Mutex lock;
public:
static constexpr auto config_prefix = "mgr.";
void insert_config(const std::map<std::string, std::string> &new_config);
// Public so that MonCommandCompletion can use it
- // FIXME: bit weird that we're sending command completions
- // to all modules (we rely on them to ignore anything that
- // they don't recognise), but when we get called from
- // python-land we don't actually know who we are. Need
- // to give python-land a handle in initialisation.
+ // FIXME: for send_command completion notifications,
+ // send it to only the module that sent the command, not everyone
void notify_all(const std::string ¬ify_type,
const std::string ¬ify_id);
- int main(std::vector<const char *> args);
+ int init();
+ void start();
+ void shutdown();
void dump_server(const std::string &hostname,
const DaemonStateCollection &dmc,