initial code repo
[stor4nfv.git] / src / ceph / src / mgr / StandbyPyModules.cc
diff --git a/src/ceph/src/mgr/StandbyPyModules.cc b/src/ceph/src/mgr/StandbyPyModules.cc
new file mode 100644 (file)
index 0000000..e567269
--- /dev/null
@@ -0,0 +1,200 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2016 John Spray <john.spray@redhat.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ */
+
+#include "StandbyPyModules.h"
+
+#include "common/debug.h"
+
+#include "mgr/MgrContext.h"
+#include "mgr/Gil.h"
+
+
+#include <boost/python.hpp>
+#include "include/assert.h"  // boost clobbers this
+
+// For ::config_prefix
+#include "PyModuleRegistry.h"
+
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_mgr
+#undef dout_prefix
+#define dout_prefix *_dout << "mgr " << __func__ << " "
+
+// Declaration fulfilled by ActivePyModules
+std::string handle_pyerror();
+
+
+StandbyPyModules::StandbyPyModules(MonClient *monc_, const MgrMap &mgr_map_)
+    : monc(monc_), load_config_thread(monc, &state)
+{
+  state.set_mgr_map(mgr_map_);
+}
+
+// FIXME: completely identical to ActivePyModules
+void StandbyPyModules::shutdown()
+{
+  Mutex::Locker locker(lock);
+
+  if (!state.is_config_loaded && load_config_thread.is_started()) {
+    // FIXME: handle cases where initial load races with shutdown
+    // this is actually not super rare because 
+    assert(0);
+    //load_config_thread.kill(SIGKILL);
+  }
+
+  // Signal modules to drop out of serve() and/or tear down resources
+  for (auto &i : modules) {
+    auto module = i.second.get();
+    const auto& name = i.first;
+    dout(10) << "waiting for module " << name << " to shutdown" << dendl;
+    lock.Unlock();
+    module->shutdown();
+    lock.Lock();
+    dout(10) << "module " << name << " shutdown" << dendl;
+  }
+
+  // For modules implementing serve(), finish the threads where we
+  // were running that.
+  for (auto &i : modules) {
+    lock.Unlock();
+    dout(10) << "joining thread for module " << i.first << dendl;
+    i.second->thread.join();
+    dout(10) << "joined thread for module " << i.first << dendl;
+    lock.Lock();
+  }
+
+  modules.clear();
+}
+
+int StandbyPyModules::start_one(std::string const &module_name,
+    PyObject *pClass, const SafeThreadState &pMyThreadState)
+{
+  Mutex::Locker l(lock);
+
+  assert(modules.count(module_name) == 0);
+
+  modules[module_name].reset(new StandbyPyModule(
+      state,
+      module_name, pClass,
+      pMyThreadState));
+
+  if (modules.size() == 1) {
+    load_config_thread.create("LoadConfig");
+  }
+
+  int r = modules[module_name]->load();
+  if (r != 0) {
+    modules.erase(module_name);
+    return r;
+  } else {
+    dout(4) << "Starting thread for " << module_name << dendl;
+    // Giving Thread the module's module_name member as its
+    // char* thread name: thread must not outlive module class lifetime.
+    modules[module_name]->thread.create(
+        modules[module_name]->get_name().c_str());
+    return 0;
+  }
+}
+
+int StandbyPyModule::load()
+{
+  Gil gil(pMyThreadState, true);
+
+  // We tell the module how we name it, so that it can be consistent
+  // with us in logging etc.
+  auto pThisPtr = PyCapsule_New(this, nullptr, nullptr);
+  assert(pThisPtr != nullptr);
+  auto pModuleName = PyString_FromString(module_name.c_str());
+  assert(pModuleName != nullptr);
+  auto pArgs = PyTuple_Pack(2, pModuleName, pThisPtr);
+  Py_DECREF(pThisPtr);
+  Py_DECREF(pModuleName);
+
+  pClassInstance = PyObject_CallObject(pClass, pArgs);
+  Py_DECREF(pArgs);
+  if (pClassInstance == nullptr) {
+    derr << "Failed to construct class in '" << module_name << "'" << dendl;
+    derr << handle_pyerror() << dendl;
+    return -EINVAL;
+  } else {
+    dout(1) << "Constructed class from module: " << module_name << dendl;
+    return 0;
+  }
+}
+
+void *StandbyPyModules::LoadConfigThread::entry()
+{
+  dout(10) << "listing keys" << dendl;
+  JSONCommand cmd;
+  cmd.run(monc, "{\"prefix\": \"config-key ls\"}");
+  cmd.wait();
+  assert(cmd.r == 0);
+
+  std::map<std::string, std::string> loaded;
+  
+  for (auto &key_str : cmd.json_result.get_array()) {
+    std::string const key = key_str.get_str();
+    dout(20) << "saw key '" << key << "'" << dendl;
+
+    const std::string config_prefix = PyModuleRegistry::config_prefix;
+
+    if (key.substr(0, config_prefix.size()) == config_prefix) {
+      dout(20) << "fetching '" << key << "'" << dendl;
+      Command get_cmd;
+      std::ostringstream cmd_json;
+      cmd_json << "{\"prefix\": \"config-key get\", \"key\": \"" << key << "\"}";
+      get_cmd.run(monc, cmd_json.str());
+      get_cmd.wait();
+      assert(get_cmd.r == 0);
+      loaded[key] = get_cmd.outbl.to_str();
+    }
+  }
+  state->loaded_config(loaded);
+
+  return nullptr;
+}
+
+bool StandbyPyModule::get_config(const std::string &key,
+                                 std::string *value) const
+{
+  PyThreadState *tstate = PyEval_SaveThread();
+  PyEval_RestoreThread(tstate);
+
+  const std::string global_key = PyModuleRegistry::config_prefix
+    + module_name + "/" + key;
+
+  dout(4) << __func__ << "key: " << global_key << dendl;
+
+  return state.with_config([global_key, value](const PyModuleConfig &config){
+    if (config.count(global_key)) {
+      *value = config.at(global_key);
+      return true;
+    } else {
+      return false;
+    }
+  });
+}
+
+std::string StandbyPyModule::get_active_uri() const
+{
+  std::string result;
+  state.with_mgr_map([&result, this](const MgrMap &mgr_map){
+    auto iter = mgr_map.services.find(module_name);
+    if (iter != mgr_map.services.end()) {
+      result = iter->second;
+    }
+  });
+
+  return result;
+}
+