]> git.proxmox.com Git - ceph.git/blob - ceph/src/mgr/PyOSDMap.cc
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / mgr / PyOSDMap.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "Mgr.h"
5
6 #include "osd/OSDMap.h"
7 #include "common/errno.h"
8 #include "common/version.h"
9 #include "include/stringify.h"
10
11 #include "PyOSDMap.h"
12 #include "PyFormatter.h"
13 #include "Gil.h"
14
15 #define dout_context g_ceph_context
16 #define dout_subsys ceph_subsys_mgr
17
18
19 typedef struct {
20 PyObject_HEAD
21 OSDMap *osdmap;
22 } BasePyOSDMap;
23
24 typedef struct {
25 PyObject_HEAD
26 OSDMap::Incremental *inc;
27 } BasePyOSDMapIncremental;
28
29 typedef struct {
30 PyObject_HEAD
31 std::shared_ptr<CrushWrapper> crush;
32 } BasePyCRUSH;
33
34 // ----------
35
36 static PyObject *osdmap_get_epoch(BasePyOSDMap *self, PyObject *obj)
37 {
38 return PyInt_FromLong(self->osdmap->get_epoch());
39 }
40
41 static PyObject *osdmap_get_crush_version(BasePyOSDMap* self, PyObject *obj)
42 {
43 return PyInt_FromLong(self->osdmap->get_crush_version());
44 }
45
46 static PyObject *osdmap_dump(BasePyOSDMap* self, PyObject *obj)
47 {
48 PyFormatter f;
49 self->osdmap->dump(&f);
50 return f.get();
51 }
52
53 static PyObject *osdmap_new_incremental(BasePyOSDMap *self, PyObject *obj)
54 {
55 OSDMap::Incremental *inc = new OSDMap::Incremental;
56
57 inc->fsid = self->osdmap->get_fsid();
58 inc->epoch = self->osdmap->get_epoch() + 1;
59 // always include latest crush map here... this is okay since we never
60 // actually use this map in the real world (and even if we did it would
61 // be a no-op).
62 self->osdmap->crush->encode(inc->crush, CEPH_FEATURES_ALL);
63 dout(10) << __func__ << " " << inc << dendl;
64
65 return construct_with_capsule("mgr_module", "OSDMapIncremental",
66 (void*)(inc));
67 }
68
69 static PyObject *osdmap_apply_incremental(BasePyOSDMap *self,
70 BasePyOSDMapIncremental *incobj)
71 {
72 if (!PyObject_TypeCheck(incobj, &BasePyOSDMapIncrementalType)) {
73 derr << "Wrong type in osdmap_apply_incremental!" << dendl;
74 return nullptr;
75 }
76
77 bufferlist bl;
78 self->osdmap->encode(bl, CEPH_FEATURES_ALL|CEPH_FEATURE_RESERVED);
79 OSDMap *next = new OSDMap;
80 next->decode(bl);
81 next->apply_incremental(*(incobj->inc));
82 dout(10) << __func__ << " map " << self->osdmap << " inc " << incobj->inc
83 << " next " << next << dendl;
84
85 return construct_with_capsule("mgr_module", "OSDMap", (void*)next);
86 }
87
88 static PyObject *osdmap_get_crush(BasePyOSDMap* self, PyObject *obj)
89 {
90 return construct_with_capsule("mgr_module", "CRUSHMap",
91 (void*)(&(self->osdmap->crush)));
92 }
93
94 static PyObject *osdmap_get_pools_by_take(BasePyOSDMap* self, PyObject *args)
95 {
96 int take;
97 if (!PyArg_ParseTuple(args, "i:get_pools_by_take",
98 &take)) {
99 return nullptr;
100 }
101
102 PyFormatter f;
103 f.open_array_section("pools");
104 for (auto& p : self->osdmap->get_pools()) {
105 if (self->osdmap->crush->rule_has_take(p.second.crush_rule, take)) {
106 f.dump_int("pool", p.first);
107 }
108 }
109 f.close_section();
110 return f.get();
111 }
112
113 static PyObject *osdmap_calc_pg_upmaps(BasePyOSDMap* self, PyObject *args)
114 {
115 PyObject *pool_list;
116 BasePyOSDMapIncremental *incobj;
117 int max_deviation = 0;
118 int max_iterations = 0;
119 if (!PyArg_ParseTuple(args, "OiiO:calc_pg_upmaps",
120 &incobj, &max_deviation,
121 &max_iterations, &pool_list)) {
122 return nullptr;
123 }
124 if (!PyList_CheckExact(pool_list)) {
125 derr << __func__ << " pool_list not a list" << dendl;
126 return nullptr;
127 }
128 set<int64_t> pools;
129 for (auto i = 0; i < PyList_Size(pool_list); ++i) {
130 PyObject *pool_name = PyList_GET_ITEM(pool_list, i);
131 if (!PyString_Check(pool_name)) {
132 derr << __func__ << " " << pool_name << " not a string" << dendl;
133 return nullptr;
134 }
135 auto pool_id = self->osdmap->lookup_pg_pool_name(
136 PyString_AsString(pool_name));
137 if (pool_id < 0) {
138 derr << __func__ << " pool '" << PyString_AsString(pool_name)
139 << "' does not exist" << dendl;
140 return nullptr;
141 }
142 pools.insert(pool_id);
143 }
144
145 dout(10) << __func__ << " osdmap " << self->osdmap << " inc " << incobj->inc
146 << " max_deviation " << max_deviation
147 << " max_iterations " << max_iterations
148 << " pools " << pools
149 << dendl;
150 PyThreadState *tstate = PyEval_SaveThread();
151 int r = self->osdmap->calc_pg_upmaps(g_ceph_context,
152 max_deviation,
153 max_iterations,
154 pools,
155 incobj->inc);
156 PyEval_RestoreThread(tstate);
157 dout(10) << __func__ << " r = " << r << dendl;
158 return PyInt_FromLong(r);
159 }
160
161 static PyObject *osdmap_map_pool_pgs_up(BasePyOSDMap* self, PyObject *args)
162 {
163 int poolid;
164 if (!PyArg_ParseTuple(args, "i:map_pool_pgs_up",
165 &poolid)) {
166 return nullptr;
167 }
168 auto pi = self->osdmap->get_pg_pool(poolid);
169 if (!pi)
170 return nullptr;
171 map<pg_t,vector<int>> pm;
172 for (unsigned ps = 0; ps < pi->get_pg_num(); ++ps) {
173 pg_t pgid(ps, poolid);
174 self->osdmap->pg_to_up_acting_osds(pgid, &pm[pgid], nullptr, nullptr, nullptr);
175 }
176 PyFormatter f;
177 for (auto p : pm) {
178 string pg = stringify(p.first);
179 f.open_array_section(pg.c_str());
180 for (auto o : p.second) {
181 f.dump_int("osd", o);
182 }
183 f.close_section();
184 }
185 return f.get();
186 }
187
188 static int
189 BasePyOSDMap_init(BasePyOSDMap *self, PyObject *args, PyObject *kwds)
190 {
191 PyObject *osdmap_capsule = nullptr;
192 static const char *kwlist[] = {"osdmap_capsule", NULL};
193
194 if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
195 const_cast<char**>(kwlist),
196 &osdmap_capsule)) {
197 ceph_abort();
198 return -1;
199 }
200 ceph_assert(PyObject_TypeCheck(osdmap_capsule, &PyCapsule_Type));
201
202 self->osdmap = (OSDMap*)PyCapsule_GetPointer(
203 osdmap_capsule, nullptr);
204 ceph_assert(self->osdmap);
205
206 return 0;
207 }
208
209
210 static void
211 BasePyOSDMap_dealloc(BasePyOSDMap *self)
212 {
213 if (self->osdmap) {
214 delete self->osdmap;
215 self->osdmap = nullptr;
216 } else {
217 derr << "Destroying improperly initialized BasePyOSDMap " << self << dendl;
218 }
219 Py_TYPE(self)->tp_free(self);
220 }
221
222 static PyObject *osdmap_pg_to_up_acting_osds(BasePyOSDMap *self, PyObject *args)
223 {
224 int pool_id = 0;
225 int ps = 0;
226 if (!PyArg_ParseTuple(args, "ii:pg_to_up_acting_osds",
227 &pool_id, &ps)) {
228 return nullptr;
229 }
230
231 std::vector<int> up;
232 int up_primary;
233 std::vector<int> acting;
234 int acting_primary;
235 pg_t pg_id(ps, pool_id);
236 self->osdmap->pg_to_up_acting_osds(pg_id,
237 &up, &up_primary,
238 &acting, &acting_primary);
239
240 // (Ab)use PyFormatter as a convenient way to generate a dict
241 PyFormatter f;
242 f.dump_int("up_primary", up_primary);
243 f.dump_int("acting_primary", acting_primary);
244 f.open_array_section("up");
245 for (const auto &i : up) {
246 f.dump_int("osd", i);
247 }
248 f.close_section();
249 f.open_array_section("acting");
250 for (const auto &i : acting) {
251 f.dump_int("osd", i);
252 }
253 f.close_section();
254
255 return f.get();
256 }
257
258 static PyObject *osdmap_pool_raw_used_rate(BasePyOSDMap *self, PyObject *args)
259 {
260 int pool_id = 0;
261 if (!PyArg_ParseTuple(args, "i:pool_raw_used_rate",
262 &pool_id)) {
263 return nullptr;
264 }
265
266 if (!self->osdmap->have_pg_pool(pool_id)) {
267 return nullptr;
268 }
269
270 float rate = self->osdmap->pool_raw_used_rate(pool_id);
271
272 return PyFloat_FromDouble(rate);
273 }
274
275
276 PyMethodDef BasePyOSDMap_methods[] = {
277 {"_get_epoch", (PyCFunction)osdmap_get_epoch, METH_NOARGS, "Get OSDMap epoch"},
278 {"_get_crush_version", (PyCFunction)osdmap_get_crush_version, METH_NOARGS,
279 "Get CRUSH version"},
280 {"_dump", (PyCFunction)osdmap_dump, METH_NOARGS, "Dump OSDMap::Incremental"},
281 {"_new_incremental", (PyCFunction)osdmap_new_incremental, METH_NOARGS,
282 "Create OSDMap::Incremental"},
283 {"_apply_incremental", (PyCFunction)osdmap_apply_incremental, METH_O,
284 "Apply OSDMap::Incremental and return the resulting OSDMap"},
285 {"_get_crush", (PyCFunction)osdmap_get_crush, METH_NOARGS, "Get CrushWrapper"},
286 {"_get_pools_by_take", (PyCFunction)osdmap_get_pools_by_take, METH_VARARGS,
287 "Get pools that have CRUSH rules that TAKE the given root"},
288 {"_calc_pg_upmaps", (PyCFunction)osdmap_calc_pg_upmaps, METH_VARARGS,
289 "Calculate new pg-upmap values"},
290 {"_map_pool_pgs_up", (PyCFunction)osdmap_map_pool_pgs_up, METH_VARARGS,
291 "Calculate up set mappings for all PGs in a pool"},
292 {"_pg_to_up_acting_osds", (PyCFunction)osdmap_pg_to_up_acting_osds, METH_VARARGS,
293 "Calculate up+acting OSDs for a PG ID"},
294 {"_pool_raw_used_rate", (PyCFunction)osdmap_pool_raw_used_rate, METH_VARARGS,
295 "Get raw space to logical space ratio"},
296 {NULL, NULL, 0, NULL}
297 };
298
299 PyTypeObject BasePyOSDMapType = {
300 PyVarObject_HEAD_INIT(NULL, 0)
301 "ceph_module.BasePyOSDMap", /* tp_name */
302 sizeof(BasePyOSDMap), /* tp_basicsize */
303 0, /* tp_itemsize */
304 (destructor)BasePyOSDMap_dealloc, /* tp_dealloc */
305 0, /* tp_print */
306 0, /* tp_getattr */
307 0, /* tp_setattr */
308 0, /* tp_compare */
309 0, /* tp_repr */
310 0, /* tp_as_number */
311 0, /* tp_as_sequence */
312 0, /* tp_as_mapping */
313 0, /* tp_hash */
314 0, /* tp_call */
315 0, /* tp_str */
316 0, /* tp_getattro */
317 0, /* tp_setattro */
318 0, /* tp_as_buffer */
319 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
320 "Ceph OSDMap", /* tp_doc */
321 0, /* tp_traverse */
322 0, /* tp_clear */
323 0, /* tp_richcompare */
324 0, /* tp_weaklistoffset */
325 0, /* tp_iter */
326 0, /* tp_iternext */
327 BasePyOSDMap_methods, /* tp_methods */
328 0, /* tp_members */
329 0, /* tp_getset */
330 0, /* tp_base */
331 0, /* tp_dict */
332 0, /* tp_descr_get */
333 0, /* tp_descr_set */
334 0, /* tp_dictoffset */
335 (initproc)BasePyOSDMap_init, /* tp_init */
336 0, /* tp_alloc */
337 0, /* tp_new */
338 };
339
340 // ----------
341
342
343 static int
344 BasePyOSDMapIncremental_init(BasePyOSDMapIncremental *self,
345 PyObject *args, PyObject *kwds)
346 {
347 PyObject *inc_capsule = nullptr;
348 static const char *kwlist[] = {"inc_capsule", NULL};
349
350 if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
351 const_cast<char**>(kwlist),
352 &inc_capsule)) {
353 ceph_abort();
354 return -1;
355 }
356 ceph_assert(PyObject_TypeCheck(inc_capsule, &PyCapsule_Type));
357
358 self->inc = (OSDMap::Incremental*)PyCapsule_GetPointer(
359 inc_capsule, nullptr);
360 ceph_assert(self->inc);
361
362 return 0;
363 }
364
365 static void
366 BasePyOSDMapIncremental_dealloc(BasePyOSDMapIncremental *self)
367 {
368 if (self->inc) {
369 delete self->inc;
370 self->inc = nullptr;
371 } else {
372 derr << "Destroying improperly initialized BasePyOSDMap " << self << dendl;
373 }
374 Py_TYPE(self)->tp_free(self);
375 }
376
377 static PyObject *osdmap_inc_get_epoch(BasePyOSDMapIncremental *self,
378 PyObject *obj)
379 {
380 return PyInt_FromLong(self->inc->epoch);
381 }
382
383 static PyObject *osdmap_inc_dump(BasePyOSDMapIncremental *self,
384 PyObject *obj)
385 {
386 PyFormatter f;
387 self->inc->dump(&f);
388 return f.get();
389 }
390
391 static int get_int_float_map(PyObject *obj, map<int,double> *out)
392 {
393 PyObject *ls = PyDict_Items(obj);
394 for (int j = 0; j < PyList_Size(ls); ++j) {
395 PyObject *pair = PyList_GET_ITEM(ls, j);
396 if (!PyTuple_Check(pair)) {
397 derr << __func__ << " item " << j << " not a tuple" << dendl;
398 Py_DECREF(ls);
399 return -1;
400 }
401 int k;
402 double v;
403 if (!PyArg_ParseTuple(pair, "id:pair", &k, &v)) {
404 derr << __func__ << " item " << j << " not a size 2 tuple" << dendl;
405 Py_DECREF(ls);
406 return -1;
407 }
408 (*out)[k] = v;
409 }
410
411 Py_DECREF(ls);
412 return 0;
413 }
414
415 static PyObject *osdmap_inc_set_osd_reweights(BasePyOSDMapIncremental *self,
416 PyObject *weightobj)
417 {
418 map<int,double> wm;
419 if (get_int_float_map(weightobj, &wm) < 0) {
420 return nullptr;
421 }
422
423 for (auto i : wm) {
424 self->inc->new_weight[i.first] = std::max(0.0, std::min(1.0, i.second)) * 0x10000;
425 }
426 Py_RETURN_NONE;
427 }
428
429 static PyObject *osdmap_inc_set_compat_weight_set_weights(
430 BasePyOSDMapIncremental *self, PyObject *weightobj)
431 {
432 map<int,double> wm;
433 if (get_int_float_map(weightobj, &wm) < 0) {
434 return nullptr;
435 }
436
437 CrushWrapper crush;
438 ceph_assert(self->inc->crush.length()); // see new_incremental
439 auto p = self->inc->crush.cbegin();
440 decode(crush, p);
441 crush.create_choose_args(CrushWrapper::DEFAULT_CHOOSE_ARGS, 1);
442 for (auto i : wm) {
443 crush.choose_args_adjust_item_weightf(
444 g_ceph_context,
445 crush.choose_args_get(CrushWrapper::DEFAULT_CHOOSE_ARGS),
446 i.first,
447 { i.second },
448 nullptr);
449 }
450 self->inc->crush.clear();
451 crush.encode(self->inc->crush, CEPH_FEATURES_ALL);
452 Py_RETURN_NONE;
453 }
454
455 PyMethodDef BasePyOSDMapIncremental_methods[] = {
456 {"_get_epoch", (PyCFunction)osdmap_inc_get_epoch, METH_NOARGS,
457 "Get OSDMap::Incremental epoch"},
458 {"_dump", (PyCFunction)osdmap_inc_dump, METH_NOARGS,
459 "Dump OSDMap::Incremental"},
460 {"_set_osd_reweights", (PyCFunction)osdmap_inc_set_osd_reweights,
461 METH_O, "Set osd reweight values"},
462 {"_set_crush_compat_weight_set_weights",
463 (PyCFunction)osdmap_inc_set_compat_weight_set_weights, METH_O,
464 "Set weight values in the pending CRUSH compat weight-set"},
465 {NULL, NULL, 0, NULL}
466 };
467
468 PyTypeObject BasePyOSDMapIncrementalType = {
469 PyVarObject_HEAD_INIT(NULL, 0)
470 "ceph_module.BasePyOSDMapIncremental", /* tp_name */
471 sizeof(BasePyOSDMapIncremental), /* tp_basicsize */
472 0, /* tp_itemsize */
473 (destructor)BasePyOSDMapIncremental_dealloc, /* tp_dealloc */
474 0, /* tp_print */
475 0, /* tp_getattr */
476 0, /* tp_setattr */
477 0, /* tp_compare */
478 0, /* tp_repr */
479 0, /* tp_as_number */
480 0, /* tp_as_sequence */
481 0, /* tp_as_mapping */
482 0, /* tp_hash */
483 0, /* tp_call */
484 0, /* tp_str */
485 0, /* tp_getattro */
486 0, /* tp_setattro */
487 0, /* tp_as_buffer */
488 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
489 "Ceph OSDMapIncremental", /* tp_doc */
490 0, /* tp_traverse */
491 0, /* tp_clear */
492 0, /* tp_richcompare */
493 0, /* tp_weaklistoffset */
494 0, /* tp_iter */
495 0, /* tp_iternext */
496 BasePyOSDMapIncremental_methods, /* tp_methods */
497 0, /* tp_members */
498 0, /* tp_getset */
499 0, /* tp_base */
500 0, /* tp_dict */
501 0, /* tp_descr_get */
502 0, /* tp_descr_set */
503 0, /* tp_dictoffset */
504 (initproc)BasePyOSDMapIncremental_init, /* tp_init */
505 0, /* tp_alloc */
506 0, /* tp_new */
507 };
508
509
510 // ----------
511
512 static int
513 BasePyCRUSH_init(BasePyCRUSH *self,
514 PyObject *args, PyObject *kwds)
515 {
516 PyObject *crush_capsule = nullptr;
517 static const char *kwlist[] = {"crush_capsule", NULL};
518
519 if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
520 const_cast<char**>(kwlist),
521 &crush_capsule)) {
522 ceph_abort();
523 return -1;
524 }
525 ceph_assert(PyObject_TypeCheck(crush_capsule, &PyCapsule_Type));
526
527 auto ptr_ref = (std::shared_ptr<CrushWrapper>*)(
528 PyCapsule_GetPointer(crush_capsule, nullptr));
529
530 // We passed a pointer to a shared pointer, which is weird, but
531 // just enough to get it into the constructor: this is a real shared
532 // pointer construction now, and then we throw away that pointer to
533 // the shared pointer.
534 self->crush = *ptr_ref;
535 ceph_assert(self->crush);
536
537 return 0;
538 }
539
540 static void
541 BasePyCRUSH_dealloc(BasePyCRUSH *self)
542 {
543 self->crush.reset();
544 Py_TYPE(self)->tp_free(self);
545 }
546
547 static PyObject *crush_dump(BasePyCRUSH *self, PyObject *obj)
548 {
549 PyFormatter f;
550 self->crush->dump(&f);
551 return f.get();
552 }
553
554 static PyObject *crush_get_item_name(BasePyCRUSH *self, PyObject *args)
555 {
556 int item;
557 if (!PyArg_ParseTuple(args, "i:get_item_name", &item)) {
558 return nullptr;
559 }
560 if (!self->crush->item_exists(item)) {
561 Py_RETURN_NONE;
562 }
563 return PyString_FromString(self->crush->get_item_name(item));
564 }
565
566 static PyObject *crush_get_item_weight(BasePyCRUSH *self, PyObject *args)
567 {
568 int item;
569 if (!PyArg_ParseTuple(args, "i:get_item_weight", &item)) {
570 return nullptr;
571 }
572 if (!self->crush->item_exists(item)) {
573 Py_RETURN_NONE;
574 }
575 return PyFloat_FromDouble(self->crush->get_item_weightf(item));
576 }
577
578 static PyObject *crush_find_takes(BasePyCRUSH *self, PyObject *obj)
579 {
580 set<int> takes;
581 self->crush->find_takes(&takes);
582 PyFormatter f;
583 f.open_array_section("takes");
584 for (auto root : takes) {
585 f.dump_int("root", root);
586 }
587 f.close_section();
588 return f.get();
589 }
590
591 static PyObject *crush_get_take_weight_osd_map(BasePyCRUSH *self, PyObject *args)
592 {
593 int root;
594 if (!PyArg_ParseTuple(args, "i:get_take_weight_osd_map",
595 &root)) {
596 return nullptr;
597 }
598 map<int,float> wmap;
599
600 if (!self->crush->item_exists(root)) {
601 return nullptr;
602 }
603
604 self->crush->get_take_weight_osd_map(root, &wmap);
605 PyFormatter f;
606 f.open_object_section("weights");
607 for (auto& p : wmap) {
608 string n = stringify(p.first); // ick
609 f.dump_float(n.c_str(), p.second);
610 }
611 f.close_section();
612 return f.get();
613 }
614
615 PyMethodDef BasePyCRUSH_methods[] = {
616 {"_dump", (PyCFunction)crush_dump, METH_NOARGS, "Dump map"},
617 {"_get_item_name", (PyCFunction)crush_get_item_name, METH_VARARGS,
618 "Get item name"},
619 {"_get_item_weight", (PyCFunction)crush_get_item_weight, METH_VARARGS,
620 "Get item weight"},
621 {"_find_takes", (PyCFunction)crush_find_takes, METH_NOARGS,
622 "Find distinct TAKE roots"},
623 {"_get_take_weight_osd_map", (PyCFunction)crush_get_take_weight_osd_map,
624 METH_VARARGS, "Get OSD weight map for a given TAKE root node"},
625 {NULL, NULL, 0, NULL}
626 };
627
628 PyTypeObject BasePyCRUSHType = {
629 PyVarObject_HEAD_INIT(NULL, 0)
630 "ceph_module.BasePyCRUSH", /* tp_name */
631 sizeof(BasePyCRUSH), /* tp_basicsize */
632 0, /* tp_itemsize */
633 (destructor)BasePyCRUSH_dealloc, /* tp_dealloc */
634 0, /* tp_print */
635 0, /* tp_getattr */
636 0, /* tp_setattr */
637 0, /* tp_compare */
638 0, /* tp_repr */
639 0, /* tp_as_number */
640 0, /* tp_as_sequence */
641 0, /* tp_as_mapping */
642 0, /* tp_hash */
643 0, /* tp_call */
644 0, /* tp_str */
645 0, /* tp_getattro */
646 0, /* tp_setattro */
647 0, /* tp_as_buffer */
648 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
649 "Ceph OSDMapIncremental", /* tp_doc */
650 0, /* tp_traverse */
651 0, /* tp_clear */
652 0, /* tp_richcompare */
653 0, /* tp_weaklistoffset */
654 0, /* tp_iter */
655 0, /* tp_iternext */
656 BasePyCRUSH_methods, /* tp_methods */
657 0, /* tp_members */
658 0, /* tp_getset */
659 0, /* tp_base */
660 0, /* tp_dict */
661 0, /* tp_descr_get */
662 0, /* tp_descr_set */
663 0, /* tp_dictoffset */
664 (initproc)BasePyCRUSH_init, /* tp_init */
665 0, /* tp_alloc */
666 0, /* tp_new */
667 };