]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr: In plugins 'module' classes need not to be called "Module" anymore. 18526/head
authorbhavishyagopesh <bhavishyagopesh@gmail.com>
Mon, 9 Oct 2017 05:40:41 +0000 (11:10 +0530)
committerKefu Chai <kchai@redhat.com>
Thu, 2 Nov 2017 04:24:12 +0000 (12:24 +0800)
Fixes: http://tracker.ceph.com/issues/17454
Signed-off-by: Kefu Chai <kchai@redhat.com>
Signed-off-by: bhavishyagopesh <bhavishyagopesh@gmail.com>
doc/mgr/plugins.rst
src/mgr/PyModuleRegistry.cc
src/mgr/PyModuleRegistry.h

index 654c50537bc7f8dde97a9011dae67d9b2e495973..7570b470d056f189f8039cc9df0e9c53e730336a 100644 (file)
@@ -6,7 +6,7 @@ Creating a plugin
 -----------------
 
 In pybind/mgr/, create a python module.  Within your module, create a class
-named ``Module`` that inherits from ``MgrModule``.
+that inherits from ``MgrModule``.
 
 The most important methods to override are:
 
@@ -33,7 +33,7 @@ or older versions of Ceph.
 Logging
 -------
 
-MgrModule instances have a ``log`` property which is a logger instance that
+``MgrModule`` instances have a ``log`` property which is a logger instance that
 sends log messages into the Ceph logging layer where they will be recorded
 in the mgr daemon's log file.
 
@@ -169,7 +169,7 @@ serve HTTP redirect responses from the standby managers so that
 the user can point his browser at any of the manager daemons without
 having to worry about which one is active.
 
-Standby manager daemons look for a class called ``StandbyModule``
+Standby manager daemons look for a subclass of ``StandbyModule``
 in each module.  If the class is not found then the module is not
 used at all on standby daemons.  If the class is found, then
 its ``serve`` method is called.  Implementations of ``StandbyModule``
index bab057c6c2846a60586faec82a41c45290458aa4..9b9de3535948209cfae7414e07214890319e5eac 100644 (file)
@@ -254,43 +254,80 @@ int PyModule::load(PyThreadState *pMainThreadState)
   // Environment is all good, import the external module
   {
     Gil gil(pMyThreadState);
-
-    // Load the module
-    PyObject *pName = PyString_FromString(module_name.c_str());
-    auto pModule = PyImport_Import(pName);
-    Py_DECREF(pName);
-    if (pModule == nullptr) {
-      derr << "Module not found: '" << module_name << "'" << dendl;
-      derr << handle_pyerror() << dendl;
-      return -ENOENT;
-    }
-
-    // Find the class
-    // TODO: let them call it what they want instead of just 'Module'
-    pClass = PyObject_GetAttrString(pModule, (const char*)"Module");
-    if (pClass == nullptr) {
+    int r;
+    r = load_subclass_of("MgrModule", &pClass);
+    if (r) {
       derr << "Class not found in module '" << module_name << "'" << dendl;
-      derr << handle_pyerror() << dendl;
-      return -EINVAL;
+      return r;
     }
-
-    pStandbyClass = PyObject_GetAttrString(pModule,
-                                           (const char*)"StandbyModule");
-    if (pStandbyClass) {
+    r = load_subclass_of("MgrStandbyModule", &pStandbyClass);
+    if (!r) {
       dout(4) << "Standby mode available in module '" << module_name
               << "'" << dendl;
     } else {
       dout(4) << "Standby mode not provided by module '" << module_name
               << "'" << dendl;
-      PyErr_Clear();
     }
-
-    Py_DECREF(pModule);
   }
-
   return 0;
 } 
 
+int PyModule::load_subclass_of(const char* base_class, PyObject** py_class)
+{
+  // load the base class
+  PyObject *mgr_module = PyImport_ImportModule("mgr_module");
+  if (!mgr_module) {
+    derr << "Module not found: 'mgr_module'" << dendl;
+    derr << handle_pyerror() << dendl;
+    return -EINVAL;
+  }
+  auto mgr_module_type = PyObject_GetAttrString(mgr_module, base_class);
+  Py_DECREF(mgr_module);
+  if (!mgr_module_type) {
+    derr << "Unable to import MgrModule from mgr_module" << dendl;
+    derr << handle_pyerror() << dendl;
+    return -EINVAL;
+  }
+
+  // find the sub class
+  PyObject *plugin_module = PyImport_ImportModule(module_name.c_str());
+  if (!plugin_module) {
+    derr << "Module not found: '" << module_name << "'" << dendl;
+    derr << handle_pyerror() << dendl;
+    return -ENOENT;
+  }
+  auto locals = PyModule_GetDict(plugin_module);
+  Py_DECREF(plugin_module);
+  PyObject *key, *value;
+  Py_ssize_t pos = 0;
+  *py_class = nullptr;
+  while (PyDict_Next(locals, &pos, &key, &value)) {
+    if (!PyType_Check(value)) {
+      continue;
+    }
+    if (!PyObject_IsSubclass(value, mgr_module_type)) {
+      continue;
+    }
+    if (PyObject_RichCompareBool(value, mgr_module_type, Py_EQ)) {
+      continue;
+    }
+    auto class_name = PyString_AsString(key);
+    if (*py_class) {
+      derr << __func__ << ": ignoring '"
+          << module_name << "." << class_name << "'"
+          << ": only one '" << base_class
+          << "' class is loaded from each plugin" << dendl;
+      continue;
+    }
+    *py_class = value;
+    dout(4) << __func__ << ": found class: '"
+           << module_name << "." << class_name << "'" << dendl;
+  }
+  Py_DECREF(mgr_module_type);
+
+  return *py_class ? 0 : -EINVAL;
+}
+
 PyModule::~PyModule()
 {
   if (pMyThreadState.ts != nullptr) {
index 5564e7f1fa5de324b8fa319c9abddabff0c1dc07..4dec10081165404d5dfa512eed5438a41a23733c 100644 (file)
@@ -31,7 +31,7 @@ class PyModule
 private:
   const std::string module_name;
   std::string get_site_packages();
-
+  int load_subclass_of(const char* class_name, PyObject** py_class);
 public:
   SafeThreadState pMyThreadState;
   PyObject *pClass = nullptr;