Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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   ceph::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   double max_deviation = 0;
118   int max_iterations = 0;
119   if (!PyArg_ParseTuple(args, "OdiO:calc_pg_upmaps",
120                         &incobj, &max_deviation,
121                         &max_iterations, &pool_list)) {
122     return nullptr;
123   }
124
125   dout(10) << __func__ << " osdmap " << self->osdmap << " inc " << incobj->inc
126            << " max_deviation " << max_deviation
127            << " max_iterations " << max_iterations
128            << dendl;
129   set<int64_t> pools;
130   // FIXME: unpack pool_list and translate to pools set
131   int r = self->osdmap->calc_pg_upmaps(g_ceph_context,
132                                  max_deviation,
133                                  max_iterations,
134                                  pools,
135                                  incobj->inc);
136   dout(10) << __func__ << " r = " << r << dendl;
137   return PyInt_FromLong(r);
138 }
139
140 static PyObject *osdmap_map_pool_pgs_up(BasePyOSDMap* self, PyObject *args)
141 {
142   int poolid;
143   if (!PyArg_ParseTuple(args, "i:map_pool_pgs_up",
144                         &poolid)) {
145     return nullptr;
146   }
147   auto pi = self->osdmap->get_pg_pool(poolid);
148   if (!pi)
149     return nullptr;
150   map<pg_t,vector<int>> pm;
151   for (unsigned ps = 0; ps < pi->get_pg_num(); ++ps) {
152     pg_t pgid(ps, poolid);
153     self->osdmap->pg_to_up_acting_osds(pgid, &pm[pgid], nullptr, nullptr, nullptr);
154   }
155   PyFormatter f;
156   for (auto p : pm) {
157     string pg = stringify(p.first);
158     f.open_array_section(pg.c_str());
159     for (auto o : p.second) {
160       f.dump_int("osd", o);
161     }
162     f.close_section();
163   }
164   return f.get();
165 }
166
167 static int
168 BasePyOSDMap_init(BasePyOSDMap *self, PyObject *args, PyObject *kwds)
169 {
170     PyObject *osdmap_capsule = nullptr;
171     static const char *kwlist[] = {"osdmap_capsule", NULL};
172
173     if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
174                                       const_cast<char**>(kwlist),
175                                       &osdmap_capsule)) {
176       assert(0);
177         return -1;
178     }
179     assert(PyObject_TypeCheck(osdmap_capsule, &PyCapsule_Type));
180
181     self->osdmap = (OSDMap*)PyCapsule_GetPointer(
182         osdmap_capsule, nullptr);
183     assert(self->osdmap);
184
185     return 0;
186 }
187
188
189 static void
190 BasePyOSDMap_dealloc(BasePyOSDMap *self)
191 {
192   if (self->osdmap) {
193     delete self->osdmap;
194     self->osdmap = nullptr;
195   } else {
196     derr << "Destroying improperly initialized BasePyOSDMap " << self << dendl;
197   }
198   Py_TYPE(self)->tp_free(self);
199 }
200
201
202 PyMethodDef BasePyOSDMap_methods[] = {
203   {"_get_epoch", (PyCFunction)osdmap_get_epoch, METH_NOARGS, "Get OSDMap epoch"},
204   {"_get_crush_version", (PyCFunction)osdmap_get_crush_version, METH_NOARGS,
205     "Get CRUSH version"},
206   {"_dump", (PyCFunction)osdmap_dump, METH_NOARGS, "Dump OSDMap::Incremental"},
207   {"_new_incremental", (PyCFunction)osdmap_new_incremental, METH_NOARGS,
208    "Create OSDMap::Incremental"},
209   {"_apply_incremental", (PyCFunction)osdmap_apply_incremental, METH_O,
210    "Apply OSDMap::Incremental and return the resulting OSDMap"},
211   {"_get_crush", (PyCFunction)osdmap_get_crush, METH_NOARGS, "Get CrushWrapper"},
212   {"_get_pools_by_take", (PyCFunction)osdmap_get_pools_by_take, METH_VARARGS,
213    "Get pools that have CRUSH rules that TAKE the given root"},
214   {"_calc_pg_upmaps", (PyCFunction)osdmap_calc_pg_upmaps, METH_VARARGS,
215    "Calculate new pg-upmap values"},
216   {"_map_pool_pgs_up", (PyCFunction)osdmap_map_pool_pgs_up, METH_VARARGS,
217    "Calculate up set mappings for all PGs in a pool"},
218   {NULL, NULL, 0, NULL}
219 };
220
221 PyTypeObject BasePyOSDMapType = {
222   PyVarObject_HEAD_INIT(NULL, 0)
223   "ceph_module.BasePyOSDMap", /* tp_name */
224   sizeof(BasePyOSDMap),     /* tp_basicsize */
225   0,                         /* tp_itemsize */
226   (destructor)BasePyOSDMap_dealloc,      /* tp_dealloc */
227   0,                         /* tp_print */
228   0,                         /* tp_getattr */
229   0,                         /* tp_setattr */
230   0,                         /* tp_compare */
231   0,                         /* tp_repr */
232   0,                         /* tp_as_number */
233   0,                         /* tp_as_sequence */
234   0,                         /* tp_as_mapping */
235   0,                         /* tp_hash */
236   0,                         /* tp_call */
237   0,                         /* tp_str */
238   0,                         /* tp_getattro */
239   0,                         /* tp_setattro */
240   0,                         /* tp_as_buffer */
241   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
242   "Ceph OSDMap",             /* tp_doc */
243   0,                         /* tp_traverse */
244   0,                         /* tp_clear */
245   0,                         /* tp_richcompare */
246   0,                         /* tp_weaklistoffset */
247   0,                         /* tp_iter */
248   0,                         /* tp_iternext */
249   BasePyOSDMap_methods,     /* tp_methods */
250   0,                         /* tp_members */
251   0,                         /* tp_getset */
252   0,                         /* tp_base */
253   0,                         /* tp_dict */
254   0,                         /* tp_descr_get */
255   0,                         /* tp_descr_set */
256   0,                         /* tp_dictoffset */
257   (initproc)BasePyOSDMap_init,                         /* tp_init */
258   0,                         /* tp_alloc */
259   0,     /* tp_new */
260 };
261
262 // ----------
263
264
265 static int
266 BasePyOSDMapIncremental_init(BasePyOSDMapIncremental *self,
267     PyObject *args, PyObject *kwds)
268 {
269     PyObject *inc_capsule = nullptr;
270     static const char *kwlist[] = {"inc_capsule", NULL};
271
272     if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
273                                       const_cast<char**>(kwlist),
274                                       &inc_capsule)) {
275       assert(0);
276         return -1;
277     }
278     assert(PyObject_TypeCheck(inc_capsule, &PyCapsule_Type));
279
280     self->inc = (OSDMap::Incremental*)PyCapsule_GetPointer(
281         inc_capsule, nullptr);
282     assert(self->inc);
283
284     return 0;
285 }
286
287 static void
288 BasePyOSDMapIncremental_dealloc(BasePyOSDMapIncremental *self)
289 {
290   if (self->inc) {
291     delete self->inc;
292     self->inc = nullptr;
293   } else {
294     derr << "Destroying improperly initialized BasePyOSDMap " << self << dendl;
295   }
296   Py_TYPE(self)->tp_free(self);
297 }
298
299 static PyObject *osdmap_inc_get_epoch(BasePyOSDMapIncremental *self,
300     PyObject *obj)
301 {
302   return PyInt_FromLong(self->inc->epoch);
303 }
304
305 static PyObject *osdmap_inc_dump(BasePyOSDMapIncremental *self,
306     PyObject *obj)
307 {
308   PyFormatter f;
309   self->inc->dump(&f);
310   return f.get();
311 }
312
313 static int get_int_float_map(PyObject *obj, map<int,double> *out)
314 {
315   PyObject *ls = PyDict_Items(obj);
316   for (int j = 0; j < PyList_Size(ls); ++j) {
317     PyObject *pair = PyList_GET_ITEM(ls, j);
318     if (!PyTuple_Check(pair)) {
319       derr << __func__ << " item " << j << " not a tuple" << dendl;
320       Py_DECREF(ls);
321       return -1;
322     }
323     int k;
324     double v;
325     if (!PyArg_ParseTuple(pair, "id:pair", &k, &v)) {
326       derr << __func__ << " item " << j << " not a size 2 tuple" << dendl;
327       Py_DECREF(ls);
328       return -1;
329     }
330     (*out)[k] = v;
331   }
332
333   Py_DECREF(ls);
334   return 0;
335 }
336
337 static PyObject *osdmap_inc_set_osd_reweights(BasePyOSDMapIncremental *self,
338     PyObject *weightobj)
339 {
340   map<int,double> wm;
341   if (get_int_float_map(weightobj, &wm) < 0) {
342     return nullptr;
343   }
344
345   for (auto i : wm) {
346     self->inc->new_weight[i.first] = std::max(0.0, std::min(1.0, i.second)) * 0x10000;
347   }
348   Py_RETURN_NONE;
349 }
350
351 static PyObject *osdmap_inc_set_compat_weight_set_weights(
352   BasePyOSDMapIncremental *self, PyObject *weightobj)
353 {
354   map<int,double> wm;
355   if (get_int_float_map(weightobj, &wm) < 0) {
356     return nullptr;
357   }
358
359   CrushWrapper crush;
360   assert(self->inc->crush.length());  // see new_incremental
361   auto p = self->inc->crush.begin();
362   ::decode(crush, p);
363   crush.create_choose_args(CrushWrapper::DEFAULT_CHOOSE_ARGS, 1);
364   for (auto i : wm) {
365     crush.choose_args_adjust_item_weightf(
366       g_ceph_context,
367       crush.choose_args_get(CrushWrapper::DEFAULT_CHOOSE_ARGS),
368       i.first,
369       { i.second },
370       nullptr);
371   }
372   self->inc->crush.clear();
373   crush.encode(self->inc->crush, CEPH_FEATURES_ALL);
374   Py_RETURN_NONE;
375 }
376
377 PyMethodDef BasePyOSDMapIncremental_methods[] = {
378   {"_get_epoch", (PyCFunction)osdmap_inc_get_epoch, METH_NOARGS,
379     "Get OSDMap::Incremental epoch"},
380   {"_dump", (PyCFunction)osdmap_inc_dump, METH_NOARGS,
381     "Dump OSDMap::Incremental"},
382   {"_set_osd_reweights", (PyCFunction)osdmap_inc_set_osd_reweights,
383     METH_O, "Set osd reweight values"},
384   {"_set_crush_compat_weight_set_weights",
385    (PyCFunction)osdmap_inc_set_compat_weight_set_weights, METH_O,
386    "Set weight values in the pending CRUSH compat weight-set"},
387   {NULL, NULL, 0, NULL}
388 };
389
390 PyTypeObject BasePyOSDMapIncrementalType = {
391   PyVarObject_HEAD_INIT(NULL, 0)
392   "ceph_module.BasePyOSDMapIncremental", /* tp_name */
393   sizeof(BasePyOSDMapIncremental),     /* tp_basicsize */
394   0,                         /* tp_itemsize */
395   (destructor)BasePyOSDMapIncremental_dealloc,      /* tp_dealloc */
396   0,                         /* tp_print */
397   0,                         /* tp_getattr */
398   0,                         /* tp_setattr */
399   0,                         /* tp_compare */
400   0,                         /* tp_repr */
401   0,                         /* tp_as_number */
402   0,                         /* tp_as_sequence */
403   0,                         /* tp_as_mapping */
404   0,                         /* tp_hash */
405   0,                         /* tp_call */
406   0,                         /* tp_str */
407   0,                         /* tp_getattro */
408   0,                         /* tp_setattro */
409   0,                         /* tp_as_buffer */
410   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
411   "Ceph OSDMapIncremental",  /* tp_doc */
412   0,                         /* tp_traverse */
413   0,                         /* tp_clear */
414   0,                         /* tp_richcompare */
415   0,                         /* tp_weaklistoffset */
416   0,                         /* tp_iter */
417   0,                         /* tp_iternext */
418   BasePyOSDMapIncremental_methods,     /* tp_methods */
419   0,                         /* tp_members */
420   0,                         /* tp_getset */
421   0,                         /* tp_base */
422   0,                         /* tp_dict */
423   0,                         /* tp_descr_get */
424   0,                         /* tp_descr_set */
425   0,                         /* tp_dictoffset */
426   (initproc)BasePyOSDMapIncremental_init,                         /* tp_init */
427   0,                         /* tp_alloc */
428   0,                         /* tp_new */
429 };
430
431
432 // ----------
433
434 static int
435 BasePyCRUSH_init(BasePyCRUSH *self,
436     PyObject *args, PyObject *kwds)
437 {
438     PyObject *crush_capsule = nullptr;
439     static const char *kwlist[] = {"crush_capsule", NULL};
440
441     if (! PyArg_ParseTupleAndKeywords(args, kwds, "O",
442                                       const_cast<char**>(kwlist),
443                                       &crush_capsule)) {
444       assert(0);
445         return -1;
446     }
447     assert(PyObject_TypeCheck(crush_capsule, &PyCapsule_Type));
448
449     auto ptr_ref = (ceph::shared_ptr<CrushWrapper>*)(
450         PyCapsule_GetPointer(crush_capsule, nullptr));
451
452     // We passed a pointer to a shared pointer, which is weird, but
453     // just enough to get it into the constructor: this is a real shared
454     // pointer construction now, and then we throw away that pointer to
455     // the shared pointer.
456     self->crush = *ptr_ref;
457     assert(self->crush);
458
459     return 0;
460 }
461
462 static void
463 BasePyCRUSH_dealloc(BasePyCRUSH *self)
464 {
465   self->crush.reset();
466   Py_TYPE(self)->tp_free(self);
467 }
468
469 static PyObject *crush_dump(BasePyCRUSH *self, PyObject *obj)
470 {
471   PyFormatter f;
472   self->crush->dump(&f);
473   return f.get();
474 }
475
476 static PyObject *crush_get_item_name(BasePyCRUSH *self, PyObject *args)
477 {
478   int item;
479   if (!PyArg_ParseTuple(args, "i:get_item_name", &item)) {
480     return nullptr;
481   }
482   if (!self->crush->item_exists(item)) {
483     Py_RETURN_NONE;
484   }
485   return PyString_FromString(self->crush->get_item_name(item));
486 }
487
488 static PyObject *crush_get_item_weight(BasePyCRUSH *self, PyObject *args)
489 {
490   int item;
491   if (!PyArg_ParseTuple(args, "i:get_item_weight", &item)) {
492     return nullptr;
493   }
494   if (!self->crush->item_exists(item)) {
495     Py_RETURN_NONE;
496   }
497   return PyFloat_FromDouble(self->crush->get_item_weightf(item));
498 }
499
500 static PyObject *crush_find_takes(BasePyCRUSH *self, PyObject *obj)
501 {
502   set<int> takes;
503   self->crush->find_takes(&takes);
504   PyFormatter f;
505   f.open_array_section("takes");
506   for (auto root : takes) {
507     f.dump_int("root", root);
508   }
509   f.close_section();
510   return f.get();
511 }
512
513 static PyObject *crush_get_take_weight_osd_map(BasePyCRUSH *self, PyObject *args)
514 {
515   int root;
516   if (!PyArg_ParseTuple(args, "i:get_take_weight_osd_map",
517                         &root)) {
518     return nullptr;
519   }
520   map<int,float> wmap;
521
522   if (!self->crush->item_exists(root)) {
523     return nullptr;
524   }
525
526   self->crush->get_take_weight_osd_map(root, &wmap);
527   PyFormatter f;
528   f.open_object_section("weights");
529   for (auto& p : wmap) {
530     string n = stringify(p.first);     // ick
531     f.dump_float(n.c_str(), p.second);
532   }
533   f.close_section();
534   return f.get();
535 }
536
537 PyMethodDef BasePyCRUSH_methods[] = {
538   {"_dump", (PyCFunction)crush_dump, METH_NOARGS, "Dump map"},
539   {"_get_item_name", (PyCFunction)crush_get_item_name, METH_VARARGS,
540     "Get item name"},
541   {"_get_item_weight", (PyCFunction)crush_get_item_weight, METH_VARARGS,
542     "Get item weight"},
543   {"_find_takes", (PyCFunction)crush_find_takes, METH_NOARGS,
544     "Find distinct TAKE roots"},
545   {"_get_take_weight_osd_map", (PyCFunction)crush_get_take_weight_osd_map,
546     METH_VARARGS, "Get OSD weight map for a given TAKE root node"},
547   {NULL, NULL, 0, NULL}
548 };
549
550 PyTypeObject BasePyCRUSHType = {
551   PyVarObject_HEAD_INIT(NULL, 0)
552   "ceph_module.BasePyCRUSH", /* tp_name */
553   sizeof(BasePyCRUSH),     /* tp_basicsize */
554   0,                         /* tp_itemsize */
555   (destructor)BasePyCRUSH_dealloc,      /* tp_dealloc */
556   0,                         /* tp_print */
557   0,                         /* tp_getattr */
558   0,                         /* tp_setattr */
559   0,                         /* tp_compare */
560   0,                         /* tp_repr */
561   0,                         /* tp_as_number */
562   0,                         /* tp_as_sequence */
563   0,                         /* tp_as_mapping */
564   0,                         /* tp_hash */
565   0,                         /* tp_call */
566   0,                         /* tp_str */
567   0,                         /* tp_getattro */
568   0,                         /* tp_setattro */
569   0,                         /* tp_as_buffer */
570   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
571   "Ceph OSDMapIncremental",  /* tp_doc */
572   0,                         /* tp_traverse */
573   0,                         /* tp_clear */
574   0,                         /* tp_richcompare */
575   0,                         /* tp_weaklistoffset */
576   0,                         /* tp_iter */
577   0,                         /* tp_iternext */
578   BasePyCRUSH_methods,     /* tp_methods */
579   0,                         /* tp_members */
580   0,                         /* tp_getset */
581   0,                         /* tp_base */
582   0,                         /* tp_dict */
583   0,                         /* tp_descr_get */
584   0,                         /* tp_descr_set */
585   0,                         /* tp_dictoffset */
586   (initproc)BasePyCRUSH_init,                         /* tp_init */
587   0,                         /* tp_alloc */
588   0,                         /* tp_new */
589 };