]> git.proxmox.com Git - ceph.git/blob - ceph/src/crush/CrushTreeDumper.h
update sources to v12.1.2
[ceph.git] / 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...
131 int s = crush->get_bucket_size(qi.id);
132 for (int k = s - 1; k >= 0; k--) {
133 int id = crush->get_bucket_item(qi.id, k);
134 if (should_dump(id)) {
135 qi.children.push_back(id);
136 push_front(Item(id, qi.id, qi.depth + 1,
137 crush->get_bucket_item_weightf(qi.id, k)));
138 }
139 }
140 }
141 return true;
142 }
143
144 void dump(F *f) {
145 reset();
146 Item qi;
147 while (next(qi))
148 dump_item(qi, f);
149 }
150
151 bool is_touched(int id) const { return touched.count(id) > 0; }
152
153 protected:
154 virtual void dump_item(const Item &qi, F *f) = 0;
155
156 protected:
157 const CrushWrapper *crush;
158 const name_map_t &weight_set_names;
159
160 private:
161 set<int> touched;
162 set<int> roots;
163 set<int>::iterator root;
164 };
165
166 inline void dump_item_fields(const CrushWrapper *crush,
167 const name_map_t& weight_set_names,
168 const Item &qi, Formatter *f) {
169 f->dump_int("id", qi.id);
170 const char *c = crush->get_item_class(qi.id);
171 if (c)
172 f->dump_string("device_class", c);
173 if (qi.is_bucket()) {
174 int type = crush->get_bucket_type(qi.id);
175 f->dump_string("name", crush->get_item_name(qi.id));
176 f->dump_string("type", crush->get_type_name(type));
177 f->dump_int("type_id", type);
178 } else {
179 f->dump_stream("name") << "osd." << qi.id;
180 f->dump_string("type", crush->get_type_name(0));
181 f->dump_int("type_id", 0);
182 f->dump_float("crush_weight", qi.weight);
183 f->dump_unsigned("depth", qi.depth);
184 }
185 if (qi.parent < 0) {
186 f->open_object_section("pool_weights");
187 for (auto& p : crush->choose_args) {
188 const crush_choose_arg_map& cmap = p.second;
189 int bidx = -1 - qi.parent;
190 const crush_bucket *b = crush->get_bucket(qi.parent);
191 if (b &&
192 bidx < (int)cmap.size &&
193 cmap.args[bidx].weight_set &&
194 cmap.args[bidx].weight_set_size >= 1) {
195 int bpos;
196 for (bpos = 0;
197 bpos < (int)cmap.args[bidx].weight_set[0].size &&
198 b->items[bpos] != qi.id;
199 ++bpos) ;
200 string name;
201 if (p.first == CrushWrapper::DEFAULT_CHOOSE_ARGS) {
202 name = "(compat)";
203 } else {
204 auto q = weight_set_names.find(p.first);
205 name = q != weight_set_names.end() ? q->second :
206 stringify(p.first);
207 }
208 f->open_array_section(name.c_str());
209 for (unsigned opos = 0;
210 opos < cmap.args[bidx].weight_set_size;
211 ++opos) {
212 float w = (float)cmap.args[bidx].weight_set[opos].weights[bpos] /
213 (float)0x10000;
214 f->dump_float("weight", w);
215 }
216 f->close_section();
217 }
218 }
219 f->close_section();
220 }
221 }
222
223 inline void dump_bucket_children(const CrushWrapper *crush,
224 const Item &qi, Formatter *f) {
225 if (!qi.is_bucket())
226 return;
227
228 f->open_array_section("children");
229 for (list<int>::const_iterator i = qi.children.begin();
230 i != qi.children.end();
231 ++i) {
232 f->dump_int("child", *i);
233 }
234 f->close_section();
235 }
236
237 class FormattingDumper : public Dumper<Formatter> {
238 public:
239 explicit FormattingDumper(const CrushWrapper *crush,
240 const name_map_t& weight_set_names)
241 : Dumper<Formatter>(crush, weight_set_names) {}
242 explicit FormattingDumper(const CrushWrapper *crush,
243 const name_map_t& weight_set_names,
244 bool show_shadow)
245 : Dumper<Formatter>(crush, weight_set_names, show_shadow) {}
246
247 protected:
248 void dump_item(const Item &qi, Formatter *f) override {
249 f->open_object_section("item");
250 dump_item_fields(qi, f);
251 dump_bucket_children(qi, f);
252 f->close_section();
253 }
254
255 virtual void dump_item_fields(const Item &qi, Formatter *f) {
256 CrushTreeDumper::dump_item_fields(crush, weight_set_names, qi, f);
257 }
258
259 virtual void dump_bucket_children(const Item &qi, Formatter *f) {
260 CrushTreeDumper::dump_bucket_children(crush, qi, f);
261 }
262 };
263
264 }
265
266 #endif