Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / crush / CrushTreeDumper.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph distributed storage system
5  *
6  * Copyright (C) 2015 Mirantis Inc
7  *
8  * Author: Mykola Golub <mgolub@mirantis.com>
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  */
16
17 #ifndef CRUSH_TREE_DUMPER_H
18 #define CRUSH_TREE_DUMPER_H
19
20 #include "CrushWrapper.h"
21 #include "include/stringify.h"
22
23 /**
24  * CrushTreeDumper:
25  * A helper class and functions to dump a crush tree.
26  *
27  * Example:
28  *
29  *  class SimpleDumper : public CrushTreeDumper::Dumper<ostream> {
30  *  public:
31  *    SimpleDumper(const CrushWrapper *crush) :
32  *      CrushTreeDumper::Dumper<ostream>(crush) {}
33  *  protected:
34  *    virtual void dump_item(const CrushTreeDumper::Item &qi, ostream *out) {
35  *      *out << qi.id;
36  *      for (int k = 0; k < qi.depth; k++)
37  *        *out << "-";
38  *      if (qi.is_bucket())
39  *        *out << crush->get_item_name(qi.id)
40  *      else
41  *        *out << "osd." << qi.id;
42  *      *out << "\n";
43  *    }
44  *  };
45  *
46  *  SimpleDumper(crush).dump(out);
47  *
48  */
49
50 namespace CrushTreeDumper {
51
52   struct Item {
53     int id;
54     int parent;
55     int depth;
56     float weight;
57     list<int> children;
58
59     Item() : id(0), parent(0), depth(0), weight(0) {}
60     Item(int i, int p, int d, float w) : id(i), parent(p), depth(d), weight(w) {}
61
62     bool is_bucket() const { return id < 0; }
63   };
64
65   template <typename F>
66   class Dumper : public list<Item> {
67   public:
68     explicit Dumper(const CrushWrapper *crush_,
69                     const name_map_t& weight_set_names_)
70       : crush(crush_), weight_set_names(weight_set_names_) {
71       crush->find_nonshadow_roots(&roots);
72       root = roots.begin();
73     }
74     explicit Dumper(const CrushWrapper *crush_,
75                     const name_map_t& weight_set_names_,
76                     bool show_shadow)
77       : crush(crush_), weight_set_names(weight_set_names_) {
78       if (show_shadow) {
79         crush->find_roots(&roots);
80       } else {
81         crush->find_nonshadow_roots(&roots);
82       }
83       root = roots.begin();
84     }
85
86     virtual ~Dumper() {}
87
88     virtual void reset() {
89       root = roots.begin();
90       touched.clear();
91       clear();
92     }
93
94     virtual bool should_dump_leaf(int i) const {
95       return true;
96     }
97     virtual bool should_dump_empty_bucket() const {
98       return true;
99     }
100
101     bool should_dump(int id) {
102       if (id >= 0)
103         return should_dump_leaf(id);
104       if (should_dump_empty_bucket())
105         return true;
106       int s = crush->get_bucket_size(id);
107       for (int k = s - 1; k >= 0; k--) {
108         int c = crush->get_bucket_item(id, k);
109         if (should_dump(c))
110           return true;
111       }
112       return false;
113     }
114
115     bool next(Item &qi) {
116       if (empty()) {
117         while (root != roots.end() && !should_dump(*root))
118           ++root;
119         if (root == roots.end())
120           return false;
121         push_back(Item(*root, 0, 0, crush->get_bucket_weightf(*root)));
122         ++root;
123       }
124
125       qi = front();
126       pop_front();
127       touched.insert(qi.id);
128
129       if (qi.is_bucket()) {
130         // queue bucket contents, sorted by (class, name)
131         int s = crush->get_bucket_size(qi.id);
132         map<string,pair<int,float>> sorted;
133         for (int k = s - 1; k >= 0; k--) {
134           int id = crush->get_bucket_item(qi.id, k);
135           if (should_dump(id)) {
136             string sort_by;
137             if (id >= 0) {
138               const char *c = crush->get_item_class(id);
139               sort_by = c ? c : "";
140               sort_by += "_";
141               char nn[80];
142               snprintf(nn, sizeof(nn), "osd.%08d", id);
143               sort_by += nn;
144             } else {
145               sort_by = "_";
146               sort_by += crush->get_item_name(id);
147             }
148             sorted[sort_by] = make_pair(
149               id, crush->get_bucket_item_weightf(qi.id, k));
150           }
151         }
152         for (auto p = sorted.rbegin(); p != sorted.rend(); ++p) {
153           qi.children.push_back(p->second.first);
154           push_front(Item(p->second.first, qi.id, qi.depth + 1,
155                           p->second.second));
156         }
157       }
158       return true;
159     }
160
161     void dump(F *f) {
162       reset();
163       Item qi;
164       while (next(qi))
165         dump_item(qi, f);
166     }
167
168     bool is_touched(int id) const { return touched.count(id) > 0; }
169
170   protected:
171     virtual void dump_item(const Item &qi, F *f) = 0;
172
173   protected:
174     const CrushWrapper *crush;
175     const name_map_t &weight_set_names;
176
177   private:
178     set<int> touched;
179     set<int> roots;
180     set<int>::iterator root;
181   };
182
183   inline void dump_item_fields(const CrushWrapper *crush,
184                                const name_map_t& weight_set_names,
185                                const Item &qi, Formatter *f) {
186     f->dump_int("id", qi.id);
187     const char *c = crush->get_item_class(qi.id);
188     if (c)
189       f->dump_string("device_class", c);
190     if (qi.is_bucket()) {
191       int type = crush->get_bucket_type(qi.id);
192       f->dump_string("name", crush->get_item_name(qi.id));
193       f->dump_string("type", crush->get_type_name(type));
194       f->dump_int("type_id", type);
195     } else {
196       f->dump_stream("name") << "osd." << qi.id;
197       f->dump_string("type", crush->get_type_name(0));
198       f->dump_int("type_id", 0);
199       f->dump_float("crush_weight", qi.weight);
200       f->dump_unsigned("depth", qi.depth);
201     }
202     if (qi.parent < 0) {
203       f->open_object_section("pool_weights");
204       for (auto& p : crush->choose_args) {
205         const crush_choose_arg_map& cmap = p.second;
206         int bidx = -1 - qi.parent;
207         const crush_bucket *b = crush->get_bucket(qi.parent);
208         if (b &&
209             bidx < (int)cmap.size &&
210             cmap.args[bidx].weight_set &&
211             cmap.args[bidx].weight_set_size >= 1) {
212           int bpos;
213           for (bpos = 0;
214                bpos < (int)cmap.args[bidx].weight_set[0].size &&
215                  b->items[bpos] != qi.id;
216                ++bpos) ;
217           string name;
218           if (p.first == CrushWrapper::DEFAULT_CHOOSE_ARGS) {
219             name = "(compat)";
220           } else {
221             auto q = weight_set_names.find(p.first);
222             name = q != weight_set_names.end() ? q->second :
223               stringify(p.first);
224           }
225           f->open_array_section(name.c_str());
226           for (unsigned opos = 0;
227                opos < cmap.args[bidx].weight_set_size;
228                ++opos) {
229             float w = (float)cmap.args[bidx].weight_set[opos].weights[bpos] /
230               (float)0x10000;
231             f->dump_float("weight", w);
232           }
233           f->close_section();
234         }
235       }
236       f->close_section();
237     }
238   }
239
240   inline void dump_bucket_children(const CrushWrapper *crush,
241                                    const Item &qi, Formatter *f) {
242     if (!qi.is_bucket())
243       return;
244
245     f->open_array_section("children");
246     for (list<int>::const_iterator i = qi.children.begin();
247          i != qi.children.end();
248          ++i) {
249       f->dump_int("child", *i);
250     }
251     f->close_section();
252   }
253
254   class FormattingDumper : public Dumper<Formatter> {
255   public:
256     explicit FormattingDumper(const CrushWrapper *crush,
257                               const name_map_t& weight_set_names)
258       : Dumper<Formatter>(crush, weight_set_names) {}
259     explicit FormattingDumper(const CrushWrapper *crush,
260                               const name_map_t& weight_set_names,
261                               bool show_shadow)
262       : Dumper<Formatter>(crush, weight_set_names, show_shadow) {}
263
264   protected:
265     void dump_item(const Item &qi, Formatter *f) override {
266       f->open_object_section("item");
267       dump_item_fields(qi, f);
268       dump_bucket_children(qi, f);
269       f->close_section();
270     }
271
272     virtual void dump_item_fields(const Item &qi, Formatter *f) {
273       CrushTreeDumper::dump_item_fields(crush, weight_set_names, qi, f);
274     }
275
276     virtual void dump_bucket_children(const Item &qi, Formatter *f) {
277       CrushTreeDumper::dump_bucket_children(crush, qi, f);
278     }
279   };
280
281 }
282
283 #endif