]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr: refactor PyOSDMap etc implementation
authorJohn Spray <john.spray@redhat.com>
Fri, 13 Oct 2017 15:31:22 +0000 (11:31 -0400)
committerJohn Spray <john.spray@redhat.com>
Wed, 1 Nov 2017 12:21:42 +0000 (08:21 -0400)
Implement real python classes from the C side,
rather than exposing only module methods.

Signed-off-by: John Spray <john.spray@redhat.com>
src/mgr/ActivePyModule.cc
src/mgr/ActivePyModules.cc
src/mgr/PyModuleRegistry.cc
src/mgr/PyModuleRunner.cc
src/mgr/PyOSDMap.cc
src/mgr/PyOSDMap.h
src/pybind/mgr/mgr_module.py
src/pybind/mgr/selftest/__init__.py [new file with mode: 0644]
src/pybind/mgr/selftest/module.py [new file with mode: 0644]

index 3a6fe6a9eca24f52d8e825e131dabca0c82beab7..46d6b9f8aef85c1386469f377ead50383b996c0a 100644 (file)
@@ -134,7 +134,19 @@ int ActivePyModule::load_commands()
   // Don't need a Gil here -- this is called from ActivePyModule::load(),
   // which already has one.
   PyObject *command_list = PyObject_GetAttrString(pClassInstance, "COMMANDS");
-  assert(command_list != nullptr);
+  if (command_list == nullptr) {
+    // Even modules that don't define command should still have the COMMANDS
+    // from the MgrModule definition.  Something is wrong!
+    derr << "Module " << get_name() << " has missing COMMANDS member" << dendl;
+    return -EINVAL;
+  }
+  if (!PyObject_TypeCheck(command_list, &PyList_Type)) {
+    // Relatively easy mistake for human to make, e.g. defining COMMANDS
+    // as a {} instead of a []
+    derr << "Module " << get_name() << " has COMMANDS member of wrong type ("
+            "should be a list)" << dendl;
+    return -EINVAL;
+  }
   const size_t list_size = PyList_Size(command_list);
   for (size_t i = 0; i < list_size; ++i) {
     PyObject *command = PyList_GetItem(command_list, i);
@@ -210,3 +222,4 @@ void ActivePyModule::get_health_checks(health_check_map_t *checks)
 {
   checks->merge(health_checks);
 }
+
index 072f10d1c7955c89f117294a85aa9a81192ebe10..5bb543c457c10422d8a6a060405ac4890bcabf70 100644 (file)
@@ -354,18 +354,21 @@ void ActivePyModules::shutdown()
   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();
+    dout(10) << "calling module " << name << " shutdown()" << dendl;
     module->shutdown();
+    dout(10) << "module " << name << " shutdown() returned" << dendl;
     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 module " << i.first << dendl;
     i.second->thread.join();
+    dout(10) << "joined module " << i.first << dendl;
     lock.Lock();
   }
 
@@ -644,12 +647,49 @@ PyObject *ActivePyModules::get_context()
   return capsule;
 }
 
-static void delete_osdmap(PyObject *object)
+/**
+ * Helper for our wrapped types that take a capsule in their constructor.
+ */
+PyObject *construct_with_capsule(
+    const std::string &module_name,
+    const std::string &clsname,
+    void *wrapped)
 {
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(object, nullptr));
-  assert(osdmap);
-  dout(10) << __func__ << " " << osdmap << dendl;
-  delete osdmap;
+  // Look up the OSDMap type which we will construct
+  PyObject *module = PyImport_ImportModule(module_name.c_str());
+  if (!module) {
+    derr << "Failed to import python module:" << dendl;
+    derr << handle_pyerror() << dendl;
+  }
+  assert(module);
+
+  PyObject *wrapper_type = PyObject_GetAttrString(
+      module, (const char*)clsname.c_str());
+  if (!wrapper_type) {
+    derr << "Failed to get python type:" << dendl;
+    derr << handle_pyerror() << dendl;
+  }
+  assert(wrapper_type);
+
+  // Construct a capsule containing an OSDMap.
+  auto wrapped_capsule = PyCapsule_New(wrapped, nullptr, nullptr);
+  assert(wrapped_capsule);
+
+  // Construct the python OSDMap
+  auto pArgs = PyTuple_Pack(1, wrapped_capsule);
+  auto wrapper_instance = PyObject_CallObject(wrapper_type, pArgs);
+  if (wrapper_instance == nullptr) {
+    derr << "Failed to construct python OSDMap:" << dendl;
+    derr << handle_pyerror() << dendl;
+  }
+  assert(wrapper_instance != nullptr);
+  Py_DECREF(pArgs);
+  Py_DECREF(wrapped_capsule);
+
+  Py_DECREF(wrapper_type);
+  Py_DECREF(module);
+
+  return wrapper_instance;
 }
 
 PyObject *ActivePyModules::get_osdmap()
@@ -658,13 +698,13 @@ PyObject *ActivePyModules::get_osdmap()
   Mutex::Locker l(lock);
   PyEval_RestoreThread(tstate);
 
-  // Construct a capsule containing an OSDMap.
   OSDMap *newmap = new OSDMap;
+
   cluster_state.with_osdmap([&](const OSDMap& o) {
       newmap->deepish_copy_from(o);
     });
-  dout(10) << __func__ << " " << newmap << dendl;
-  return PyCapsule_New(newmap, nullptr, &delete_osdmap);
+
+  return construct_with_capsule("mgr_module", "OSDMap", (void*)newmap);
 }
 
 void ActivePyModules::set_health_checks(const std::string& module_name,
index b0e9ef493279ca8a153a25ed2404fc49b9894cd6..3f4e60b86d2234a427f748e3f6c4f50b05fa7cfe 100644 (file)
@@ -231,28 +231,22 @@ int PyModule::load(PyThreadState *pMainThreadState)
     PyObject *ceph_module = Py_InitModule("ceph_module", ModuleMethods);
     assert(ceph_module != nullptr);
 
-    Py_InitModule("ceph_osdmap", OSDMapMethods);
-    Py_InitModule("ceph_osdmap_incremental", OSDMapIncrementalMethods);
-    Py_InitModule("ceph_crushmap", CRUSHMapMethods);
-
-    // Initialize base classes
-    BaseMgrModuleType.tp_new = PyType_GenericNew;
-    if (PyType_Ready(&BaseMgrModuleType) < 0) {
-        assert(0);
-    }
-
-    Py_INCREF(&BaseMgrModuleType);
-    PyModule_AddObject(ceph_module, "BaseMgrModule",
-                       (PyObject *)&BaseMgrModuleType);
+    auto load_class = [ceph_module](const char *name, PyTypeObject *type)
+    {
+      type->tp_new = PyType_GenericNew;
+      if (PyType_Ready(type) < 0) {
+          assert(0);
+      }
+      Py_INCREF(type);
 
-    BaseMgrModuleType.tp_new = PyType_GenericNew;
-    if (PyType_Ready(&BaseMgrStandbyModuleType) < 0) {
-        assert(0);
-    }
+      PyModule_AddObject(ceph_module, name, (PyObject *)type);
+    };
 
-    Py_INCREF(&BaseMgrStandbyModuleType);
-    PyModule_AddObject(ceph_module, "BaseMgrStandbyModule",
-                       (PyObject *)&BaseMgrStandbyModuleType);
+    load_class("BaseMgrModule", &BaseMgrModuleType);
+    load_class("BaseMgrStandbyModule", &BaseMgrStandbyModuleType);
+    load_class("BasePyOSDMap", &BasePyOSDMapType);
+    load_class("BasePyOSDMapIncremental", &BasePyOSDMapIncrementalType);
+    load_class("BasePyCRUSH", &BasePyCRUSHType);
   }
 
   // Environment is all good, import the external module
index c20199e9c6662a8f034a4bd361c9dd17c03231db..59d86a8bede0a5cc7dd4acabf336596205548c24 100644 (file)
@@ -28,7 +28,7 @@ std::string handle_pyerror();
 
 PyModuleRunner::~PyModuleRunner()
 {
-  Gil gil(pMyThreadState);
+  Gil gil(pMyThreadState, true);
 
   if (pClassInstance) {
     Py_XDECREF(pClassInstance);
index 121bf80651df8f836054a88f31c0b5fa1697bbe3..8bae2e4b532d82786bceaa677d23bd7639bcaa84 100644 (file)
 #define dout_context g_ceph_context
 #define dout_subsys ceph_subsys_mgr
 
+
+typedef struct {
+  PyObject_HEAD
+  OSDMap *osdmap;
+} BasePyOSDMap;
+
+typedef struct {
+  PyObject_HEAD
+  OSDMap::Incremental *inc;
+} BasePyOSDMapIncremental;
+
+typedef struct {
+  PyObject_HEAD
+  ceph::shared_ptr<CrushWrapper> crush;
+} BasePyCRUSH;
+
 // ----------
 
-static PyObject *osdmap_get_epoch(PyObject *self, PyObject *obj)
+static PyObject *osdmap_get_epoch(BasePyOSDMap *self, PyObject *obj)
 {
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(obj, nullptr));
-  return PyInt_FromLong(osdmap->get_epoch());
+  return PyInt_FromLong(self->osdmap->get_epoch());
 }
 
-static PyObject *osdmap_get_crush_version(PyObject *self, PyObject *obj)
+static PyObject *osdmap_get_crush_version(BasePyOSDMap* self, PyObject *obj)
 {
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(obj, nullptr));
-  return PyInt_FromLong(osdmap->get_crush_version());
+  return PyInt_FromLong(self->osdmap->get_crush_version());
 }
 
-static PyObject *osdmap_dump(PyObject *self, PyObject *obj)
+static PyObject *osdmap_dump(BasePyOSDMap* self, PyObject *obj)
 {
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(obj, nullptr));
   PyFormatter f;
-  osdmap->dump(&f);
+  self->osdmap->dump(&f);
   return f.get();
 }
 
-
-static void delete_osdmap_incremental(PyObject *object)
-{
-  OSDMap::Incremental *inc = static_cast<OSDMap::Incremental*>(
-    PyCapsule_GetPointer(object, nullptr));
-  dout(10) << __func__ << " " << inc << dendl;
-  delete inc;
-}
-
-static PyObject *osdmap_new_incremental(PyObject *self, PyObject *obj)
+static PyObject *osdmap_new_incremental(BasePyOSDMap *self, PyObject *obj)
 {
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(obj, nullptr));
   OSDMap::Incremental *inc = new OSDMap::Incremental;
-  inc->fsid = osdmap->get_fsid();
-  inc->epoch = osdmap->get_epoch() + 1;
+
+  inc->fsid = self->osdmap->get_fsid();
+  inc->epoch = self->osdmap->get_epoch() + 1;
   // always include latest crush map here... this is okay since we never
   // actually use this map in the real world (and even if we did it would
   // be a no-op).
-  osdmap->crush->encode(inc->crush, CEPH_FEATURES_ALL);
+  self->osdmap->crush->encode(inc->crush, CEPH_FEATURES_ALL);
   dout(10) << __func__ << " " << inc << dendl;
-  return PyCapsule_New(inc, nullptr, &delete_osdmap_incremental);
-}
 
-static void delete_osdmap(PyObject *object)
-{
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(object, nullptr));
-  assert(osdmap);
-  dout(10) << __func__ << " " << osdmap << dendl;
-  delete osdmap;
+  return construct_with_capsule("mgr_module", "OSDMapIncremental",
+                                (void*)(inc));
 }
 
-static PyObject *osdmap_apply_incremental(PyObject *self, PyObject *args)
+static PyObject *osdmap_apply_incremental(BasePyOSDMap *self,
+    BasePyOSDMapIncremental *incobj)
 {
-  PyObject *mapobj, *incobj;
-  if (!PyArg_ParseTuple(args, "OO:apply_incremental",
-                       &mapobj, &incobj)) {
-    return nullptr;
-  }
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(mapobj, nullptr));
-  OSDMap::Incremental *inc = static_cast<OSDMap::Incremental*>(
-    PyCapsule_GetPointer(incobj, nullptr));
-  if (!osdmap || !inc) {
+  if (!PyObject_TypeCheck(incobj, &BasePyOSDMapIncrementalType)) {
+    derr << "Wrong type in osdmap_apply_incremental!" << dendl;
     return nullptr;
   }
 
   bufferlist bl;
-  osdmap->encode(bl, CEPH_FEATURES_ALL|CEPH_FEATURE_RESERVED);
+  self->osdmap->encode(bl, CEPH_FEATURES_ALL|CEPH_FEATURE_RESERVED);
   OSDMap *next = new OSDMap;
   next->decode(bl);
-  next->apply_incremental(*inc);
-  dout(10) << __func__ << " map " << osdmap << " inc " << inc
+  next->apply_incremental(*(incobj->inc));
+  dout(10) << __func__ << " map " << self->osdmap << " inc " << incobj->inc
           << " next " << next << dendl;
-  return PyCapsule_New(next, nullptr, &delete_osdmap);
+
+  return construct_with_capsule("mgr_module", "OSDMap", (void*)next);
 }
 
-static PyObject *osdmap_get_crush(PyObject *self, PyObject *obj)
+static PyObject *osdmap_get_crush(BasePyOSDMap* self, PyObject *obj)
 {
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(obj, nullptr));
-
-  // Construct a capsule containing a the CrushWrapper.
-  return PyCapsule_New(osdmap->crush.get(), nullptr, nullptr);
+  return construct_with_capsule("mgr_module", "CRUSHMap",
+      (void*)(&(self->osdmap->crush)));
 }
 
-static PyObject *osdmap_get_pools_by_take(PyObject *self, PyObject *args)
+static PyObject *osdmap_get_pools_by_take(BasePyOSDMap* self, PyObject *args)
 {
-  PyObject *mapobj;
   int take;
-  if (!PyArg_ParseTuple(args, "Oi:get_pools_by_take",
-                       &mapobj, &take)) {
+  if (!PyArg_ParseTuple(args, "i:get_pools_by_take",
+                       &take)) {
     return nullptr;
   }
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(mapobj, nullptr));
+
   PyFormatter f;
   f.open_array_section("pools");
-  for (auto& p : osdmap->get_pools()) {
-    if (osdmap->crush->rule_has_take(p.second.crush_rule, take)) {
+  for (auto& p : self->osdmap->get_pools()) {
+    if (self->osdmap->crush->rule_has_take(p.second.crush_rule, take)) {
       f.dump_int("pool", p.first);
     }
   }
@@ -120,54 +110,47 @@ static PyObject *osdmap_get_pools_by_take(PyObject *self, PyObject *args)
   return f.get();
 }
 
-static PyObject *osdmap_calc_pg_upmaps(PyObject *self, PyObject *args)
+static PyObject *osdmap_calc_pg_upmaps(BasePyOSDMap* self, PyObject *args)
 {
-  PyObject *mapobj, *incobj, *pool_list;
+  PyObject *pool_list;
+  BasePyOSDMapIncremental *incobj;
   double max_deviation = 0;
   int max_iterations = 0;
-  if (!PyArg_ParseTuple(args, "OOdiO:calc_pg_upmaps",
-                       &mapobj, &incobj, &max_deviation,
+  if (!PyArg_ParseTuple(args, "OdiO:calc_pg_upmaps",
+                       &incobj, &max_deviation,
                        &max_iterations, &pool_list)) {
     return nullptr;
   }
 
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(mapobj, nullptr));
-  OSDMap::Incremental *inc = static_cast<OSDMap::Incremental*>(
-    PyCapsule_GetPointer(incobj, nullptr));
-
-  dout(10) << __func__ << " osdmap " << osdmap << " inc " << inc
+  dout(10) << __func__ << " osdmap " << self->osdmap << " inc " << incobj->inc
           << " max_deviation " << max_deviation
           << " max_iterations " << max_iterations
           << dendl;
   set<int64_t> pools;
   // FIXME: unpack pool_list and translate to pools set
-  int r = osdmap->calc_pg_upmaps(g_ceph_context,
+  int r = self->osdmap->calc_pg_upmaps(g_ceph_context,
                                 max_deviation,
                                 max_iterations,
                                 pools,
-                                inc);
+                                incobj->inc);
   dout(10) << __func__ << " r = " << r << dendl;
   return PyInt_FromLong(r);
 }
 
-static PyObject *osdmap_map_pool_pgs_up(PyObject *self, PyObject *args)
+static PyObject *osdmap_map_pool_pgs_up(BasePyOSDMap* self, PyObject *args)
 {
-  PyObject *mapobj;
   int poolid;
-  if (!PyArg_ParseTuple(args, "Oi:map_pool_pgs_up",
-                       &mapobj, &poolid)) {
+  if (!PyArg_ParseTuple(args, "i:map_pool_pgs_up",
+                       &poolid)) {
     return nullptr;
   }
-  OSDMap *osdmap = static_cast<OSDMap*>(PyCapsule_GetPointer(mapobj, nullptr));
-  if (!osdmap)
-    return nullptr;
-  auto pi = osdmap->get_pg_pool(poolid);
+  auto pi = self->osdmap->get_pg_pool(poolid);
   if (!pi)
     return nullptr;
   map<pg_t,vector<int>> pm;
   for (unsigned ps = 0; ps < pi->get_pg_num(); ++ps) {
     pg_t pgid(ps, poolid);
-    osdmap->pg_to_up_acting_osds(pgid, &pm[pgid], nullptr, nullptr, nullptr);
+    self->osdmap->pg_to_up_acting_osds(pgid, &pm[pgid], nullptr, nullptr, nullptr);
   }
   PyFormatter f;
   for (auto p : pm) {
@@ -181,39 +164,149 @@ static PyObject *osdmap_map_pool_pgs_up(PyObject *self, PyObject *args)
   return f.get();
 }
 
-PyMethodDef OSDMapMethods[] = {
-  {"get_epoch", osdmap_get_epoch, METH_O, "Get OSDMap epoch"},
-  {"get_crush_version", osdmap_get_crush_version, METH_O, "Get CRUSH version"},
-  {"dump", osdmap_dump, METH_O, "Dump OSDMap::Incremental"},
-  {"new_incremental", osdmap_new_incremental, METH_O,
+static int
+BasePyOSDMap_init(BasePyOSDMap *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *osdmap_capsule = nullptr;
+    static const char *kwlist[] = {"osdmap_capsule", NULL};
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
+                                      const_cast<char**>(kwlist),
+                                      &osdmap_capsule)) {
+      assert(0);
+        return -1;
+    }
+    assert(PyObject_TypeCheck(osdmap_capsule, &PyCapsule_Type));
+
+    self->osdmap = (OSDMap*)PyCapsule_GetPointer(
+        osdmap_capsule, nullptr);
+    assert(self->osdmap);
+
+    return 0;
+}
+
+
+static void
+BasePyOSDMap_dealloc(BasePyOSDMap *self)
+{
+  if (self->osdmap) {
+    delete self->osdmap;
+    self->osdmap = nullptr;
+  } else {
+    derr << "Destroying improperly initialized BasePyOSDMap " << self << dendl;
+  }
+  Py_TYPE(self)->tp_free(self);
+}
+
+
+PyMethodDef BasePyOSDMap_methods[] = {
+  {"_get_epoch", (PyCFunction)osdmap_get_epoch, METH_NOARGS, "Get OSDMap epoch"},
+  {"_get_crush_version", (PyCFunction)osdmap_get_crush_version, METH_NOARGS,
+    "Get CRUSH version"},
+  {"_dump", (PyCFunction)osdmap_dump, METH_NOARGS, "Dump OSDMap::Incremental"},
+  {"_new_incremental", (PyCFunction)osdmap_new_incremental, METH_NOARGS,
    "Create OSDMap::Incremental"},
-  {"apply_incremental", osdmap_apply_incremental, METH_VARARGS,
+  {"_apply_incremental", (PyCFunction)osdmap_apply_incremental, METH_O,
    "Apply OSDMap::Incremental and return the resulting OSDMap"},
-  {"get_crush", osdmap_get_crush, METH_O, "Get CrushWrapper"},
-  {"get_pools_by_take", osdmap_get_pools_by_take, METH_VARARGS,
+  {"_get_crush", (PyCFunction)osdmap_get_crush, METH_NOARGS, "Get CrushWrapper"},
+  {"_get_pools_by_take", (PyCFunction)osdmap_get_pools_by_take, METH_VARARGS,
    "Get pools that have CRUSH rules that TAKE the given root"},
-  {"calc_pg_upmaps", osdmap_calc_pg_upmaps, METH_VARARGS,
+  {"_calc_pg_upmaps", (PyCFunction)osdmap_calc_pg_upmaps, METH_VARARGS,
    "Calculate new pg-upmap values"},
-  {"map_pool_pgs_up", osdmap_map_pool_pgs_up, METH_VARARGS,
+  {"_map_pool_pgs_up", (PyCFunction)osdmap_map_pool_pgs_up, METH_VARARGS,
    "Calculate up set mappings for all PGs in a pool"},
   {NULL, NULL, 0, NULL}
 };
 
+PyTypeObject BasePyOSDMapType = {
+  PyVarObject_HEAD_INIT(NULL, 0)
+  "ceph_module.BasePyOSDMap", /* tp_name */
+  sizeof(BasePyOSDMap),     /* tp_basicsize */
+  0,                         /* tp_itemsize */
+  (destructor)BasePyOSDMap_dealloc,      /* tp_dealloc */
+  0,                         /* tp_print */
+  0,                         /* tp_getattr */
+  0,                         /* tp_setattr */
+  0,                         /* tp_compare */
+  0,                         /* tp_repr */
+  0,                         /* tp_as_number */
+  0,                         /* tp_as_sequence */
+  0,                         /* tp_as_mapping */
+  0,                         /* tp_hash */
+  0,                         /* tp_call */
+  0,                         /* tp_str */
+  0,                         /* tp_getattro */
+  0,                         /* tp_setattro */
+  0,                         /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
+  "Ceph OSDMap",             /* tp_doc */
+  0,                         /* tp_traverse */
+  0,                         /* tp_clear */
+  0,                         /* tp_richcompare */
+  0,                         /* tp_weaklistoffset */
+  0,                         /* tp_iter */
+  0,                         /* tp_iternext */
+  BasePyOSDMap_methods,     /* tp_methods */
+  0,                         /* tp_members */
+  0,                         /* tp_getset */
+  0,                         /* tp_base */
+  0,                         /* tp_dict */
+  0,                         /* tp_descr_get */
+  0,                         /* tp_descr_set */
+  0,                         /* tp_dictoffset */
+  (initproc)BasePyOSDMap_init,                         /* tp_init */
+  0,                         /* tp_alloc */
+  0,     /* tp_new */
+};
+
 // ----------
 
-static PyObject *osdmap_inc_get_epoch(PyObject *self, PyObject *obj)
+
+static int
+BasePyOSDMapIncremental_init(BasePyOSDMapIncremental *self,
+    PyObject *args, PyObject *kwds)
+{
+    PyObject *inc_capsule = nullptr;
+    static const char *kwlist[] = {"inc_capsule", NULL};
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
+                                      const_cast<char**>(kwlist),
+                                      &inc_capsule)) {
+      assert(0);
+        return -1;
+    }
+    assert(PyObject_TypeCheck(inc_capsule, &PyCapsule_Type));
+
+    self->inc = (OSDMap::Incremental*)PyCapsule_GetPointer(
+        inc_capsule, nullptr);
+    assert(self->inc);
+
+    return 0;
+}
+
+static void
+BasePyOSDMapIncremental_dealloc(BasePyOSDMapIncremental *self)
+{
+  if (self->inc) {
+    delete self->inc;
+    self->inc = nullptr;
+  } else {
+    derr << "Destroying improperly initialized BasePyOSDMap " << self << dendl;
+  }
+  Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *osdmap_inc_get_epoch(BasePyOSDMapIncremental *self,
+    PyObject *obj)
 {
-  OSDMap::Incremental *inc = static_cast<OSDMap::Incremental*>(
-    PyCapsule_GetPointer(obj, nullptr));
-  return PyInt_FromLong(inc->epoch);
+  return PyInt_FromLong(self->inc->epoch);
 }
 
-static PyObject *osdmap_inc_dump(PyObject *self, PyObject *obj)
+static PyObject *osdmap_inc_dump(BasePyOSDMapIncremental *self,
+    PyObject *obj)
 {
-  OSDMap::Incremental *inc = static_cast<OSDMap::Incremental*>(
-    PyCapsule_GetPointer(obj, nullptr));
   PyFormatter f;
-  inc->dump(&f);
+  self->inc->dump(&f);
   return f.get();
 }
 
@@ -224,57 +317,48 @@ static int get_int_float_map(PyObject *obj, map<int,double> *out)
     PyObject *pair = PyList_GET_ITEM(ls, j);
     if (!PyTuple_Check(pair)) {
       derr << __func__ << " item " << j << " not a tuple" << dendl;
+      Py_DECREF(ls);
       return -1;
     }
     int k;
     double v;
     if (!PyArg_ParseTuple(pair, "id:pair", &k, &v)) {
       derr << __func__ << " item " << j << " not a size 2 tuple" << dendl;
+      Py_DECREF(ls);
       return -1;
     }
     (*out)[k] = v;
   }
+
+  Py_DECREF(ls);
   return 0;
 }
 
-static PyObject *osdmap_inc_set_osd_reweights(PyObject *self, PyObject *args)
+static PyObject *osdmap_inc_set_osd_reweights(BasePyOSDMapIncremental *self,
+    PyObject *weightobj)
 {
-  PyObject *incobj, *weightobj;
-  if (!PyArg_ParseTuple(args, "OO:set_osd_reweights",
-                       &incobj, &weightobj)) {
-    return nullptr;
-  }
-  OSDMap::Incremental *inc = static_cast<OSDMap::Incremental*>(
-    PyCapsule_GetPointer(incobj, nullptr));
   map<int,double> wm;
   if (get_int_float_map(weightobj, &wm) < 0) {
     return nullptr;
   }
 
   for (auto i : wm) {
-    inc->new_weight[i.first] = std::max(0.0, std::min(1.0, i.second)) * 0x10000;
+    self->inc->new_weight[i.first] = std::max(0.0, std::min(1.0, i.second)) * 0x10000;
   }
   Py_RETURN_NONE;
 }
 
 static PyObject *osdmap_inc_set_compat_weight_set_weights(
-  PyObject *self, PyObject *args)
+  BasePyOSDMapIncremental *self, PyObject *weightobj)
 {
-  PyObject *incobj, *weightobj;
-  if (!PyArg_ParseTuple(args, "OO:set_compat_weight_set_weights",
-                       &incobj, &weightobj)) {
-    return nullptr;
-  }
-  OSDMap::Incremental *inc = static_cast<OSDMap::Incremental*>(
-    PyCapsule_GetPointer(incobj, nullptr));
   map<int,double> wm;
   if (get_int_float_map(weightobj, &wm) < 0) {
     return nullptr;
   }
 
   CrushWrapper crush;
-  assert(inc->crush.length());  // see new_incremental
-  auto p = inc->crush.begin();
+  assert(self->inc->crush.length());  // see new_incremental
+  auto p = self->inc->crush.begin();
   ::decode(crush, p);
   crush.create_choose_args(CrushWrapper::DEFAULT_CHOOSE_ARGS, 1);
   for (auto i : wm) {
@@ -285,74 +369,138 @@ static PyObject *osdmap_inc_set_compat_weight_set_weights(
       { i.second },
       nullptr);
   }
-  inc->crush.clear();
-  crush.encode(inc->crush, CEPH_FEATURES_ALL);
+  self->inc->crush.clear();
+  crush.encode(self->inc->crush, CEPH_FEATURES_ALL);
   Py_RETURN_NONE;
 }
 
-
-
-PyMethodDef OSDMapIncrementalMethods[] = {
-  {"get_epoch", osdmap_inc_get_epoch, METH_O, "Get OSDMap::Incremental epoch"},
-  {"dump", osdmap_inc_dump, METH_O, "Dump OSDMap::Incremental"},
-  {"set_osd_reweights", osdmap_inc_set_osd_reweights, METH_VARARGS,
-   "Set osd reweight values"},
-  {"set_crush_compat_weight_set_weights",
-   osdmap_inc_set_compat_weight_set_weights, METH_VARARGS,
+PyMethodDef BasePyOSDMapIncremental_methods[] = {
+  {"_get_epoch", (PyCFunction)osdmap_inc_get_epoch, METH_NOARGS,
+    "Get OSDMap::Incremental epoch"},
+  {"_dump", (PyCFunction)osdmap_inc_dump, METH_NOARGS,
+    "Dump OSDMap::Incremental"},
+  {"_set_osd_reweights", (PyCFunction)osdmap_inc_set_osd_reweights,
+    METH_O, "Set osd reweight values"},
+  {"_set_crush_compat_weight_set_weights",
+   (PyCFunction)osdmap_inc_set_compat_weight_set_weights, METH_O,
    "Set weight values in the pending CRUSH compat weight-set"},
   {NULL, NULL, 0, NULL}
 };
 
+PyTypeObject BasePyOSDMapIncrementalType = {
+  PyVarObject_HEAD_INIT(NULL, 0)
+  "ceph_module.BasePyOSDMapIncremental", /* tp_name */
+  sizeof(BasePyOSDMapIncremental),     /* tp_basicsize */
+  0,                         /* tp_itemsize */
+  (destructor)BasePyOSDMapIncremental_dealloc,      /* tp_dealloc */
+  0,                         /* tp_print */
+  0,                         /* tp_getattr */
+  0,                         /* tp_setattr */
+  0,                         /* tp_compare */
+  0,                         /* tp_repr */
+  0,                         /* tp_as_number */
+  0,                         /* tp_as_sequence */
+  0,                         /* tp_as_mapping */
+  0,                         /* tp_hash */
+  0,                         /* tp_call */
+  0,                         /* tp_str */
+  0,                         /* tp_getattro */
+  0,                         /* tp_setattro */
+  0,                         /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
+  "Ceph OSDMapIncremental",  /* tp_doc */
+  0,                         /* tp_traverse */
+  0,                         /* tp_clear */
+  0,                         /* tp_richcompare */
+  0,                         /* tp_weaklistoffset */
+  0,                         /* tp_iter */
+  0,                         /* tp_iternext */
+  BasePyOSDMapIncremental_methods,     /* tp_methods */
+  0,                         /* tp_members */
+  0,                         /* tp_getset */
+  0,                         /* tp_base */
+  0,                         /* tp_dict */
+  0,                         /* tp_descr_get */
+  0,                         /* tp_descr_set */
+  0,                         /* tp_dictoffset */
+  (initproc)BasePyOSDMapIncremental_init,                         /* tp_init */
+  0,                         /* tp_alloc */
+  0,                         /* tp_new */
+};
+
 
 // ----------
 
-static PyObject *crush_dump(PyObject *self, PyObject *obj)
+static int
+BasePyCRUSH_init(BasePyCRUSH *self,
+    PyObject *args, PyObject *kwds)
+{
+    PyObject *crush_capsule = nullptr;
+    static const char *kwlist[] = {"crush_capsule", NULL};
+
+    if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
+                                      const_cast<char**>(kwlist),
+                                      &crush_capsule)) {
+      assert(0);
+        return -1;
+    }
+    assert(PyObject_TypeCheck(crush_capsule, &PyCapsule_Type));
+
+    auto ptr_ref = (ceph::shared_ptr<CrushWrapper>*)(
+        PyCapsule_GetPointer(crush_capsule, nullptr));
+
+    // We passed a pointer to a shared pointer, which is weird, but
+    // just enough to get it into the constructor: this is a real shared
+    // pointer construction now, and then we throw away that pointer to
+    // the shared pointer.
+    self->crush = *ptr_ref;
+    assert(self->crush);
+
+    return 0;
+}
+
+static void
+BasePyCRUSH_dealloc(BasePyCRUSH *self)
+{
+  self->crush.reset();
+  Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *crush_dump(BasePyCRUSH *self, PyObject *obj)
 {
-  CrushWrapper *crush = static_cast<CrushWrapper*>(
-    PyCapsule_GetPointer(obj, nullptr));
   PyFormatter f;
-  crush->dump(&f);
+  self->crush->dump(&f);
   return f.get();
 }
 
-static PyObject *crush_get_item_name(PyObject *self, PyObject *args)
+static PyObject *crush_get_item_name(BasePyCRUSH *self, PyObject *args)
 {
-  PyObject *obj;
   int item;
-  if (!PyArg_ParseTuple(args, "Oi:get_item_name",
-                       &obj, &item)) {
+  if (!PyArg_ParseTuple(args, "i:get_item_name", &item)) {
     return nullptr;
   }
-  CrushWrapper *crush = static_cast<CrushWrapper*>(
-    PyCapsule_GetPointer(obj, nullptr));
-  if (!crush->item_exists(item)) {
+  if (!self->crush->item_exists(item)) {
     Py_RETURN_NONE;
   }
-  return PyString_FromString(crush->get_item_name(item));
+  return PyString_FromString(self->crush->get_item_name(item));
 }
 
-static PyObject *crush_get_item_weight(PyObject *self, PyObject *args)
+static PyObject *crush_get_item_weight(BasePyCRUSH *self, PyObject *args)
 {
-  PyObject *obj;
   int item;
-  if (!PyArg_ParseTuple(args, "Oi:get_item_weight",
-                       &obj, &item)) {
+  if (!PyArg_ParseTuple(args, "i:get_item_weight", &item)) {
     return nullptr;
   }
-  CrushWrapper *crush = static_cast<CrushWrapper*>(
-    PyCapsule_GetPointer(obj, nullptr));
-  if (!crush->item_exists(item)) {
+  if (!self->crush->item_exists(item)) {
     Py_RETURN_NONE;
   }
-  return PyFloat_FromDouble(crush->get_item_weightf(item));
+  return PyFloat_FromDouble(self->crush->get_item_weightf(item));
 }
 
-static PyObject *crush_find_takes(PyObject *self, PyObject *obj)
+static PyObject *crush_find_takes(BasePyCRUSH *self, PyObject *obj)
 {
-  CrushWrapper *crush = static_cast<CrushWrapper*>(
-    PyCapsule_GetPointer(obj, nullptr));
   set<int> takes;
-  crush->find_takes(&takes);
+  self->crush->find_takes(&takes);
   PyFormatter f;
   f.open_array_section("takes");
   for (auto root : takes) {
@@ -362,19 +510,20 @@ static PyObject *crush_find_takes(PyObject *self, PyObject *obj)
   return f.get();
 }
 
-static PyObject *crush_get_take_weight_osd_map(PyObject *self, PyObject *args)
+static PyObject *crush_get_take_weight_osd_map(BasePyCRUSH *self, PyObject *args)
 {
-  PyObject *obj;
   int root;
-  if (!PyArg_ParseTuple(args, "Oi:get_take_weight_osd_map",
-                       &obj, &root)) {
+  if (!PyArg_ParseTuple(args, "i:get_take_weight_osd_map",
+                       &root)) {
     return nullptr;
   }
-  CrushWrapper *crush = static_cast<CrushWrapper*>(
-    PyCapsule_GetPointer(obj, nullptr));
-
   map<int,float> wmap;
-  crush->get_take_weight_osd_map(root, &wmap);
+
+  if (!self->crush->item_exists(root)) {
+    return nullptr;
+  }
+
+  self->crush->get_take_weight_osd_map(root, &wmap);
   PyFormatter f;
   f.open_object_section("weights");
   for (auto& p : wmap) {
@@ -385,12 +534,56 @@ static PyObject *crush_get_take_weight_osd_map(PyObject *self, PyObject *args)
   return f.get();
 }
 
-PyMethodDef CRUSHMapMethods[] = {
-  {"dump", crush_dump, METH_O, "Dump map"},
-  {"get_item_name", crush_get_item_name, METH_VARARGS, "Get item name"},
-  {"get_item_weight", crush_get_item_weight, METH_VARARGS, "Get item weight"},
-  {"find_takes", crush_find_takes, METH_O, "Find distinct TAKE roots"},
-  {"get_take_weight_osd_map", crush_get_take_weight_osd_map, METH_VARARGS,
-   "Get OSD weight map for a given TAKE root node"},
+PyMethodDef BasePyCRUSH_methods[] = {
+  {"_dump", (PyCFunction)crush_dump, METH_NOARGS, "Dump map"},
+  {"_get_item_name", (PyCFunction)crush_get_item_name, METH_VARARGS,
+    "Get item name"},
+  {"_get_item_weight", (PyCFunction)crush_get_item_weight, METH_VARARGS,
+    "Get item weight"},
+  {"_find_takes", (PyCFunction)crush_find_takes, METH_NOARGS,
+    "Find distinct TAKE roots"},
+  {"_get_take_weight_osd_map", (PyCFunction)crush_get_take_weight_osd_map,
+    METH_VARARGS, "Get OSD weight map for a given TAKE root node"},
   {NULL, NULL, 0, NULL}
 };
+
+PyTypeObject BasePyCRUSHType = {
+  PyVarObject_HEAD_INIT(NULL, 0)
+  "ceph_module.BasePyCRUSH", /* tp_name */
+  sizeof(BasePyCRUSH),     /* tp_basicsize */
+  0,                         /* tp_itemsize */
+  (destructor)BasePyCRUSH_dealloc,      /* tp_dealloc */
+  0,                         /* tp_print */
+  0,                         /* tp_getattr */
+  0,                         /* tp_setattr */
+  0,                         /* tp_compare */
+  0,                         /* tp_repr */
+  0,                         /* tp_as_number */
+  0,                         /* tp_as_sequence */
+  0,                         /* tp_as_mapping */
+  0,                         /* tp_hash */
+  0,                         /* tp_call */
+  0,                         /* tp_str */
+  0,                         /* tp_getattro */
+  0,                         /* tp_setattro */
+  0,                         /* tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
+  "Ceph OSDMapIncremental",  /* tp_doc */
+  0,                         /* tp_traverse */
+  0,                         /* tp_clear */
+  0,                         /* tp_richcompare */
+  0,                         /* tp_weaklistoffset */
+  0,                         /* tp_iter */
+  0,                         /* tp_iternext */
+  BasePyCRUSH_methods,     /* tp_methods */
+  0,                         /* tp_members */
+  0,                         /* tp_getset */
+  0,                         /* tp_base */
+  0,                         /* tp_dict */
+  0,                         /* tp_descr_get */
+  0,                         /* tp_descr_set */
+  0,                         /* tp_dictoffset */
+  (initproc)BasePyCRUSH_init,                         /* tp_init */
+  0,                         /* tp_alloc */
+  0,                         /* tp_new */
+};
index bfa851cd660122b6beee8b32281089ea4f3aa99f..09e5b041c87c9589a6ebd200aaafd40ca5555a7a 100644 (file)
@@ -3,8 +3,18 @@
 
 #pragma once
 
+#include <string>
+
 #include "Python.h"
 
-extern PyMethodDef OSDMapMethods[];
-extern PyMethodDef OSDMapIncrementalMethods[];
-extern PyMethodDef CRUSHMapMethods[];
+
+
+extern PyTypeObject BasePyOSDMapType;
+extern PyTypeObject BasePyOSDMapIncrementalType;
+extern PyTypeObject BasePyCRUSHType;
+
+PyObject *construct_with_capsule(
+    const std::string &module,
+    const std::string &clsname,
+    void *wrapped);
+
index 0c2cd5d17497845063e546b9e41d9049d87494d8..958d49c234aea33c01bb45bff1f4fe7e187797e4 100644 (file)
@@ -1,8 +1,8 @@
 
 import ceph_module  # noqa
-import ceph_osdmap  #noqa
-import ceph_osdmap_incremental  #noqa
-import ceph_crushmap  #noqa
+#import ceph_osdmap  #noqa
+#import ceph_osdmap_incremental  #noqa
+#import ceph_crushmap  #noqa
 
 import json
 import logging
@@ -74,87 +74,72 @@ class CommandResult(object):
         return self.r, self.outb, self.outs
 
 
-class OSDMap(object):
-    def __init__(self, handle):
-        self._handle = handle
-
+class OSDMap(ceph_module.BasePyOSDMap):
     def get_epoch(self):
-        return ceph_osdmap.get_epoch(self._handle)
+        return self._get_epoch()
 
     def get_crush_version(self):
-        return ceph_osdmap.get_crush_version(self._handle)
+        return self._get_crush_version()
 
     def dump(self):
-        return ceph_osdmap.dump(self._handle)
+        return self._dump()
 
     def new_incremental(self):
-        return OSDMapIncremental(ceph_osdmap.new_incremental(self._handle))
+        return self._new_incremental()
 
     def apply_incremental(self, inc):
-        return OSDMap(ceph_osdmap.apply_incremental(self._handle, inc._handle))
+        return self._apply_incremental(inc)
 
     def get_crush(self):
-        return CRUSHMap(ceph_osdmap.get_crush(self._handle), self)
+        return self._get_crush()
 
     def get_pools_by_take(self, take):
-        return ceph_osdmap.get_pools_by_take(self._handle, take).get('pools', [])
+        return self._get_pools_by_take(take).get('pools', [])
 
     def calc_pg_upmaps(self, inc,
                        max_deviation=.01, max_iterations=10, pools=[]):
-        return ceph_osdmap.calc_pg_upmaps(
-            self._handle,
+        return self._calc_pg_upmaps(
             inc._handle,
             max_deviation, max_iterations, pools)
 
     def map_pool_pgs_up(self, poolid):
-        return ceph_osdmap.map_pool_pgs_up(self._handle, poolid)
-
-class OSDMapIncremental(object):
-    def __init__(self, handle):
-        self._handle = handle
+        return self._map_pool_pgs_up(poolid)
 
+class OSDMapIncremental(ceph_module.BasePyOSDMapIncremental):
     def get_epoch(self):
-        return ceph_osdmap_incremental.get_epoch(self._handle)
+        return self._get_epoch()
 
     def dump(self):
-        return ceph_osdmap_incremental.dump(self._handle)
+        return self._dump()
 
     def set_osd_reweights(self, weightmap):
         """
         weightmap is a dict, int to float.  e.g. { 0: .9, 1: 1.0, 3: .997 }
         """
-        return ceph_osdmap_incremental.set_osd_reweights(self._handle, weightmap)
+        return self._set_osd_reweights(weightmap)
 
     def set_crush_compat_weight_set_weights(self, weightmap):
         """
         weightmap is a dict, int to float.  devices only.  e.g.,
         { 0: 3.4, 1: 3.3, 2: 3.334 }
         """
-        return ceph_osdmap_incremental.set_crush_compat_weight_set_weights(
-            self._handle, weightmap)
-
-
-
-class CRUSHMap(object):
-    def __init__(self, handle, parent_osdmap):
-        self._handle = handle
-        # keep ref to parent osdmap since handle lifecycle is owned by it
-        self._parent_osdmap = parent_osdmap
+        return self._set_crush_compat_weight_set_weights(weightmap)
 
+class CRUSHMap(ceph_module.BasePyCRUSH):
     def dump(self):
-        return ceph_crushmap.dump(self._handle)
+        return self._dump()
 
     def get_item_weight(self, item):
-        return ceph_crushmap.get_item_weight(self._handle, item)
+        return self._get_item_weight(item)
 
     def get_item_name(self, item):
-        return ceph_crushmap.get_item_name(self._handle, item)
+        return self._get_item_name(item)
 
     def find_takes(self):
-        return ceph_crushmap.find_takes(self._handle).get('takes',[])
+        return self._find_takes().get('takes', [])
 
     def get_take_weight_osd_map(self, root):
-        uglymap = ceph_crushmap.get_take_weight_osd_map(self._handle, root)
+        uglymap = self._get_take_weight_osd_map(root)
         return { int(k): v for k, v in uglymap.get('weights', {}).iteritems() }
 
 class MgrStandbyModule(ceph_module.BaseMgrStandbyModule):
@@ -507,7 +492,7 @@ class MgrModule(ceph_module.BaseMgrModule):
         OSDMap.
         :return: OSDMap
         """
-        return OSDMap(self._ceph_get_osdmap())
+        return self._ceph_get_osdmap()
 
     def get_all_perf_counters(self, prio_limit=PRIO_USEFUL):
         """
diff --git a/src/pybind/mgr/selftest/__init__.py b/src/pybind/mgr/selftest/__init__.py
new file mode 100644 (file)
index 0000000..622a611
--- /dev/null
@@ -0,0 +1,3 @@
+
+from module import *
+
diff --git a/src/pybind/mgr/selftest/module.py b/src/pybind/mgr/selftest/module.py
new file mode 100644 (file)
index 0000000..cf464d0
--- /dev/null
@@ -0,0 +1,46 @@
+
+from mgr_module import MgrModule
+
+
+class Module(MgrModule):
+    COMMANDS = [{
+        "cmd": "mgr self-test",
+        "desc": "Run mgr python interface tests",
+        "perm": "r"
+    }]
+    
+    def handle_command(self, command):
+        if command['prefix'] == 'mgr self-test':
+            self._self_test()
+
+            return 0, '', 'Self-test succeeded'
+        else:
+            return (-errno.EINVAL, '',
+                    "Command not found '{0}'".format(command['prefix']))
+
+    def _self_test(self):
+        osdmap = self.get_osdmap()
+        osdmap.get_epoch()
+        osdmap.get_crush_version()
+        osdmap.dump()
+
+
+        inc = osdmap.new_incremental()
+        osdmap.apply_incremental(inc)
+        inc.get_epoch()
+        inc.dump()
+
+        crush = osdmap.get_crush()
+        crush.dump()
+        crush.get_item_name(-1)
+        crush.get_item_weight(-1)
+        crush.find_takes()
+        crush.get_take_weight_osd_map(-1)
+
+        #osdmap.get_pools_by_take()
+        #osdmap.calc_pg_upmaps()
+        #osdmap.map_pools_pgs_up()
+
+        #inc.set_osd_reweights
+        #inc.set_crush_compat_weight_set_weights
+