From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/mgr/PyOSDMap.cc | 721 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 721 insertions(+) create mode 100644 src/mgr/PyOSDMap.cc (limited to 'src/mgr/PyOSDMap.cc') diff --git a/src/mgr/PyOSDMap.cc b/src/mgr/PyOSDMap.cc new file mode 100644 index 000000000..83475f5ee --- /dev/null +++ b/src/mgr/PyOSDMap.cc @@ -0,0 +1,721 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "Mgr.h" + +#include "osd/OSDMap.h" +#include "common/errno.h" +#include "common/version.h" +#include "include/stringify.h" + +#include "PyOSDMap.h" +#include "PyFormatter.h" +#include "Gil.h" + +#define dout_context g_ceph_context +#define dout_subsys ceph_subsys_mgr + +using std::map; +using std::set; +using std::string; +using std::vector; + +typedef struct { + PyObject_HEAD + OSDMap *osdmap; +} BasePyOSDMap; + +typedef struct { + PyObject_HEAD + OSDMap::Incremental *inc; +} BasePyOSDMapIncremental; + +typedef struct { + PyObject_HEAD + std::shared_ptr crush; +} BasePyCRUSH; + +// ---------- + +static PyObject *osdmap_get_epoch(BasePyOSDMap *self, PyObject *obj) +{ + return PyLong_FromLong(self->osdmap->get_epoch()); +} + +static PyObject *osdmap_get_crush_version(BasePyOSDMap* self, PyObject *obj) +{ + return PyLong_FromLong(self->osdmap->get_crush_version()); +} + +static PyObject *osdmap_dump(BasePyOSDMap* self, PyObject *obj) +{ + PyFormatter f; + self->osdmap->dump(&f, g_ceph_context); + return f.get(); +} + +static PyObject *osdmap_new_incremental(BasePyOSDMap *self, PyObject *obj) +{ + OSDMap::Incremental *inc = new OSDMap::Incremental; + + 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). + self->osdmap->crush->encode(inc->crush, CEPH_FEATURES_ALL); + dout(10) << __func__ << " " << inc << dendl; + + return construct_with_capsule("mgr_module", "OSDMapIncremental", + (void*)(inc)); +} + +static PyObject *osdmap_apply_incremental(BasePyOSDMap *self, + BasePyOSDMapIncremental *incobj) +{ + if (!PyObject_TypeCheck(incobj, &BasePyOSDMapIncrementalType)) { + derr << "Wrong type in osdmap_apply_incremental!" << dendl; + return nullptr; + } + + bufferlist bl; + self->osdmap->encode(bl, CEPH_FEATURES_ALL|CEPH_FEATURE_RESERVED); + OSDMap *next = new OSDMap; + next->decode(bl); + next->apply_incremental(*(incobj->inc)); + dout(10) << __func__ << " map " << self->osdmap << " inc " << incobj->inc + << " next " << next << dendl; + + return construct_with_capsule("mgr_module", "OSDMap", (void*)next); +} + +static PyObject *osdmap_get_crush(BasePyOSDMap* self, PyObject *obj) +{ + return construct_with_capsule("mgr_module", "CRUSHMap", + (void*)(&(self->osdmap->crush))); +} + +static PyObject *osdmap_get_pools_by_take(BasePyOSDMap* self, PyObject *args) +{ + int take; + if (!PyArg_ParseTuple(args, "i:get_pools_by_take", + &take)) { + return nullptr; + } + + PyFormatter f; + f.open_array_section("pools"); + 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); + } + } + f.close_section(); + return f.get(); +} + +static PyObject *osdmap_calc_pg_upmaps(BasePyOSDMap* self, PyObject *args) +{ + PyObject *pool_list; + BasePyOSDMapIncremental *incobj; + int max_deviation = 0; + int max_iterations = 0; + if (!PyArg_ParseTuple(args, "OiiO:calc_pg_upmaps", + &incobj, &max_deviation, + &max_iterations, &pool_list)) { + return nullptr; + } + if (!PyList_CheckExact(pool_list)) { + derr << __func__ << " pool_list not a list" << dendl; + return nullptr; + } + set pools; + for (auto i = 0; i < PyList_Size(pool_list); ++i) { + PyObject *pool_name = PyList_GET_ITEM(pool_list, i); + if (!PyUnicode_Check(pool_name)) { + derr << __func__ << " " << pool_name << " not a string" << dendl; + return nullptr; + } + auto pool_id = self->osdmap->lookup_pg_pool_name( + PyUnicode_AsUTF8(pool_name)); + if (pool_id < 0) { + derr << __func__ << " pool '" << PyUnicode_AsUTF8(pool_name) + << "' does not exist" << dendl; + return nullptr; + } + pools.insert(pool_id); + } + + dout(10) << __func__ << " osdmap " << self->osdmap << " inc " << incobj->inc + << " max_deviation " << max_deviation + << " max_iterations " << max_iterations + << " pools " << pools + << dendl; + PyThreadState *tstate = PyEval_SaveThread(); + int r = self->osdmap->calc_pg_upmaps(g_ceph_context, + max_deviation, + max_iterations, + pools, + incobj->inc); + PyEval_RestoreThread(tstate); + dout(10) << __func__ << " r = " << r << dendl; + return PyLong_FromLong(r); +} + +static PyObject *osdmap_map_pool_pgs_up(BasePyOSDMap* self, PyObject *args) +{ + int poolid; + if (!PyArg_ParseTuple(args, "i:map_pool_pgs_up", + &poolid)) { + return nullptr; + } + auto pi = self->osdmap->get_pg_pool(poolid); + if (!pi) + return nullptr; + map> pm; + for (unsigned ps = 0; ps < pi->get_pg_num(); ++ps) { + pg_t pgid(ps, poolid); + self->osdmap->pg_to_up_acting_osds(pgid, &pm[pgid], nullptr, nullptr, nullptr); + } + PyFormatter f; + for (auto p : pm) { + string pg = stringify(p.first); + f.open_array_section(pg.c_str()); + for (auto o : p.second) { + f.dump_int("osd", o); + } + f.close_section(); + } + return f.get(); +} + +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(kwlist), + &osdmap_capsule)) { + return -1; + } + if (!PyObject_TypeCheck(osdmap_capsule, &PyCapsule_Type)) { + PyErr_Format(PyExc_TypeError, + "Expected a PyCapsule_Type, not %s", + Py_TYPE(osdmap_capsule)->tp_name); + return -1; + } + + self->osdmap = (OSDMap*)PyCapsule_GetPointer( + osdmap_capsule, nullptr); + ceph_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); +} + +static PyObject *osdmap_pg_to_up_acting_osds(BasePyOSDMap *self, PyObject *args) +{ + int pool_id = 0; + int ps = 0; + if (!PyArg_ParseTuple(args, "ii:pg_to_up_acting_osds", + &pool_id, &ps)) { + return nullptr; + } + + std::vector up; + int up_primary; + std::vector acting; + int acting_primary; + pg_t pg_id(ps, pool_id); + self->osdmap->pg_to_up_acting_osds(pg_id, + &up, &up_primary, + &acting, &acting_primary); + + // (Ab)use PyFormatter as a convenient way to generate a dict + PyFormatter f; + f.dump_int("up_primary", up_primary); + f.dump_int("acting_primary", acting_primary); + f.open_array_section("up"); + for (const auto &i : up) { + f.dump_int("osd", i); + } + f.close_section(); + f.open_array_section("acting"); + for (const auto &i : acting) { + f.dump_int("osd", i); + } + f.close_section(); + + return f.get(); +} + +static PyObject *osdmap_pool_raw_used_rate(BasePyOSDMap *self, PyObject *args) +{ + int pool_id = 0; + if (!PyArg_ParseTuple(args, "i:pool_raw_used_rate", + &pool_id)) { + return nullptr; + } + + if (!self->osdmap->have_pg_pool(pool_id)) { + return nullptr; + } + + float rate = self->osdmap->pool_raw_used_rate(pool_id); + + return PyFloat_FromDouble(rate); +} + +static PyObject *osdmap_build_simple(PyObject *cls, PyObject *args, PyObject *kwargs) +{ + static const char *kwlist[] = {"epoch", "uuid", "num_osd", nullptr}; + int epoch = 1; + char* uuid_str = nullptr; + int num_osd = -1; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "izi", + const_cast(kwlist), + &epoch, &uuid_str, &num_osd)) { + Py_RETURN_NONE; + } + uuid_d uuid; + if (uuid_str) { + if (!uuid.parse(uuid_str)) { + PyErr_Format(PyExc_ValueError, "bad uuid %s", uuid_str); + Py_RETURN_NONE; + } + } else { + uuid.generate_random(); + } + + auto osdmap = without_gil([&] { + OSDMap* osdmap = new OSDMap(); + // negative osd is allowed, in that case i just count all osds in ceph.conf + osdmap->build_simple(g_ceph_context, epoch, uuid, num_osd); + return osdmap; + }); + return construct_with_capsule("mgr_module", "OSDMap", reinterpret_cast(osdmap)); +} + +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", (PyCFunction)osdmap_apply_incremental, METH_O, + "Apply OSDMap::Incremental and return the resulting OSDMap"}, + {"_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", (PyCFunction)osdmap_calc_pg_upmaps, METH_VARARGS, + "Calculate new pg-upmap values"}, + {"_map_pool_pgs_up", (PyCFunction)osdmap_map_pool_pgs_up, METH_VARARGS, + "Calculate up set mappings for all PGs in a pool"}, + {"_pg_to_up_acting_osds", (PyCFunction)osdmap_pg_to_up_acting_osds, METH_VARARGS, + "Calculate up+acting OSDs for a PG ID"}, + {"_pool_raw_used_rate", (PyCFunction)osdmap_pool_raw_used_rate, METH_VARARGS, + "Get raw space to logical space ratio"}, + {"_build_simple", (PyCFunction)osdmap_build_simple, METH_VARARGS | METH_CLASS, + "Create a simple OSDMap"}, + {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 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(kwlist), + &inc_capsule)) { + ceph_abort(); + return -1; + } + ceph_assert(PyObject_TypeCheck(inc_capsule, &PyCapsule_Type)); + + self->inc = (OSDMap::Incremental*)PyCapsule_GetPointer( + inc_capsule, nullptr); + ceph_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) +{ + return PyLong_FromLong(self->inc->epoch); +} + +static PyObject *osdmap_inc_dump(BasePyOSDMapIncremental *self, + PyObject *obj) +{ + PyFormatter f; + self->inc->dump(&f); + return f.get(); +} + +static int get_int_float_map(PyObject *obj, map *out) +{ + PyObject *ls = PyDict_Items(obj); + for (int j = 0; j < PyList_Size(ls); ++j) { + 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(BasePyOSDMapIncremental *self, + PyObject *weightobj) +{ + map wm; + if (get_int_float_map(weightobj, &wm) < 0) { + return nullptr; + } + + for (auto i : wm) { + 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( + BasePyOSDMapIncremental *self, PyObject *weightobj) +{ + map wm; + if (get_int_float_map(weightobj, &wm) < 0) { + return nullptr; + } + + CrushWrapper crush; + ceph_assert(self->inc->crush.length()); // see new_incremental + auto p = self->inc->crush.cbegin(); + decode(crush, p); + crush.create_choose_args(CrushWrapper::DEFAULT_CHOOSE_ARGS, 1); + for (auto i : wm) { + crush.choose_args_adjust_item_weightf( + g_ceph_context, + crush.choose_args_get(CrushWrapper::DEFAULT_CHOOSE_ARGS), + i.first, + { i.second }, + nullptr); + } + self->inc->crush.clear(); + crush.encode(self->inc->crush, CEPH_FEATURES_ALL); + Py_RETURN_NONE; +} + +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 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(kwlist), + &crush_capsule)) { + ceph_abort(); + return -1; + } + ceph_assert(PyObject_TypeCheck(crush_capsule, &PyCapsule_Type)); + + auto ptr_ref = (std::shared_ptr*)( + 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; + ceph_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) +{ + PyFormatter f; + self->crush->dump(&f); + return f.get(); +} + +static PyObject *crush_get_item_name(BasePyCRUSH *self, PyObject *args) +{ + int item; + if (!PyArg_ParseTuple(args, "i:get_item_name", &item)) { + return nullptr; + } + if (!self->crush->item_exists(item)) { + Py_RETURN_NONE; + } + return PyUnicode_FromString(self->crush->get_item_name(item)); +} + +static PyObject *crush_get_item_weight(BasePyCRUSH *self, PyObject *args) +{ + int item; + if (!PyArg_ParseTuple(args, "i:get_item_weight", &item)) { + return nullptr; + } + if (!self->crush->item_exists(item)) { + Py_RETURN_NONE; + } + return PyFloat_FromDouble(self->crush->get_item_weightf(item)); +} + +static PyObject *crush_find_roots(BasePyCRUSH *self) +{ + set roots; + self->crush->find_roots(&roots); + PyFormatter f; + f.open_array_section("roots"); + for (auto root : roots) { + f.dump_int("root", root); + } + f.close_section(); + return f.get(); +} + +static PyObject *crush_find_takes(BasePyCRUSH *self, PyObject *obj) +{ + set takes; + self->crush->find_takes(&takes); + PyFormatter f; + f.open_array_section("takes"); + for (auto root : takes) { + f.dump_int("root", root); + } + f.close_section(); + return f.get(); +} + +static PyObject *crush_get_take_weight_osd_map(BasePyCRUSH *self, PyObject *args) +{ + int root; + if (!PyArg_ParseTuple(args, "i:get_take_weight_osd_map", + &root)) { + return nullptr; + } + map 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) { + string n = stringify(p.first); // ick + f.dump_float(n.c_str(), p.second); + } + f.close_section(); + return f.get(); +} + +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_roots", (PyCFunction)crush_find_roots, METH_NOARGS, + "Find all tree roots"}, + {"_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 */ +}; -- cgit v1.2.3