]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
22 | /** | |
23 | * CrushTreeDumper: | |
24 | * A helper class and functions to dump a crush tree. | |
25 | * | |
26 | * Example: | |
27 | * | |
28 | * class SimpleDumper : public CrushTreeDumper::Dumper<ostream> { | |
29 | * public: | |
30 | * SimpleDumper(const CrushWrapper *crush) : | |
31 | * CrushTreeDumper::Dumper<ostream>(crush) {} | |
32 | * protected: | |
33 | * virtual void dump_item(const CrushTreeDumper::Item &qi, ostream *out) { | |
34 | * *out << qi.id; | |
35 | * for (int k = 0; k < qi.depth; k++) | |
36 | * *out << "-"; | |
37 | * if (qi.is_bucket()) | |
38 | * *out << crush->get_item_name(qi.id) | |
39 | * else | |
40 | * *out << "osd." << qi.id; | |
41 | * *out << "\n"; | |
42 | * } | |
43 | * }; | |
44 | * | |
45 | * SimpleDumper(crush).dump(out); | |
46 | * | |
47 | */ | |
48 | ||
49 | namespace CrushTreeDumper { | |
50 | ||
51 | struct Item { | |
52 | int id; | |
53 | int depth; | |
54 | float weight; | |
55 | list<int> children; | |
56 | ||
57 | Item() : id(0), depth(0), weight(0) {} | |
58 | Item(int i, int d, float w) : id(i), depth(d), weight(w) {} | |
59 | ||
60 | bool is_bucket() const { return id < 0; } | |
61 | }; | |
62 | ||
63 | template <typename F> | |
64 | class Dumper : public list<Item> { | |
65 | public: | |
66 | explicit Dumper(const CrushWrapper *crush_) : crush(crush_) { | |
224ce89b | 67 | crush->find_nonshadow_roots(roots); |
7c673cae FG |
68 | root = roots.begin(); |
69 | } | |
70 | ||
71 | virtual ~Dumper() {} | |
72 | ||
73 | virtual void reset() { | |
74 | root = roots.begin(); | |
75 | touched.clear(); | |
76 | clear(); | |
77 | } | |
78 | ||
31f18b77 FG |
79 | virtual bool should_dump_leaf(int i) const { |
80 | return true; | |
81 | } | |
82 | virtual bool should_dump_empty_bucket() const { | |
83 | return true; | |
84 | } | |
85 | ||
86 | bool should_dump(int id) { | |
87 | if (id >= 0) | |
88 | return should_dump_leaf(id); | |
89 | if (should_dump_empty_bucket()) | |
90 | return true; | |
91 | int s = crush->get_bucket_size(id); | |
92 | for (int k = s - 1; k >= 0; k--) { | |
93 | int c = crush->get_bucket_item(id, k); | |
94 | if (should_dump(c)) | |
95 | return true; | |
96 | } | |
97 | return false; | |
98 | } | |
99 | ||
7c673cae FG |
100 | bool next(Item &qi) { |
101 | if (empty()) { | |
31f18b77 FG |
102 | while (root != roots.end() && !should_dump(*root)) |
103 | ++root; | |
7c673cae FG |
104 | if (root == roots.end()) |
105 | return false; | |
106 | push_back(Item(*root, 0, crush->get_bucket_weightf(*root))); | |
107 | ++root; | |
108 | } | |
109 | ||
110 | qi = front(); | |
111 | pop_front(); | |
112 | touched.insert(qi.id); | |
113 | ||
114 | if (qi.is_bucket()) { | |
115 | // queue bucket contents... | |
116 | int s = crush->get_bucket_size(qi.id); | |
117 | for (int k = s - 1; k >= 0; k--) { | |
118 | int id = crush->get_bucket_item(qi.id, k); | |
31f18b77 FG |
119 | if (should_dump(id)) { |
120 | qi.children.push_back(id); | |
121 | push_front(Item(id, qi.depth + 1, | |
122 | crush->get_bucket_item_weightf(qi.id, k))); | |
123 | } | |
7c673cae FG |
124 | } |
125 | } | |
126 | return true; | |
127 | } | |
128 | ||
129 | void dump(F *f) { | |
130 | reset(); | |
131 | Item qi; | |
132 | while (next(qi)) | |
133 | dump_item(qi, f); | |
134 | } | |
135 | ||
136 | bool is_touched(int id) const { return touched.count(id) > 0; } | |
137 | ||
138 | protected: | |
139 | virtual void dump_item(const Item &qi, F *f) = 0; | |
140 | ||
141 | protected: | |
142 | const CrushWrapper *crush; | |
143 | ||
144 | private: | |
145 | set<int> touched; | |
146 | set<int> roots; | |
147 | set<int>::iterator root; | |
148 | }; | |
149 | ||
150 | inline void dump_item_fields(const CrushWrapper *crush, | |
151 | const Item &qi, Formatter *f) { | |
152 | f->dump_int("id", qi.id); | |
224ce89b WB |
153 | const char *c = crush->get_item_class(qi.id); |
154 | if (!c) | |
155 | c = ""; | |
156 | f->dump_string("device_class", c); | |
7c673cae FG |
157 | if (qi.is_bucket()) { |
158 | int type = crush->get_bucket_type(qi.id); | |
159 | f->dump_string("name", crush->get_item_name(qi.id)); | |
160 | f->dump_string("type", crush->get_type_name(type)); | |
161 | f->dump_int("type_id", type); | |
162 | } else { | |
163 | f->dump_stream("name") << "osd." << qi.id; | |
164 | f->dump_string("type", crush->get_type_name(0)); | |
165 | f->dump_int("type_id", 0); | |
166 | f->dump_float("crush_weight", qi.weight); | |
167 | f->dump_unsigned("depth", qi.depth); | |
168 | } | |
169 | } | |
170 | ||
171 | inline void dump_bucket_children(const CrushWrapper *crush, | |
172 | const Item &qi, Formatter *f) { | |
173 | if (!qi.is_bucket()) | |
174 | return; | |
175 | ||
176 | f->open_array_section("children"); | |
177 | for (list<int>::const_iterator i = qi.children.begin(); | |
178 | i != qi.children.end(); | |
179 | ++i) { | |
180 | f->dump_int("child", *i); | |
181 | } | |
182 | f->close_section(); | |
183 | } | |
184 | ||
185 | class FormattingDumper : public Dumper<Formatter> { | |
186 | public: | |
187 | explicit FormattingDumper(const CrushWrapper *crush) : Dumper<Formatter>(crush) {} | |
188 | ||
189 | protected: | |
190 | void dump_item(const Item &qi, Formatter *f) override { | |
191 | f->open_object_section("item"); | |
192 | dump_item_fields(qi, f); | |
193 | dump_bucket_children(qi, f); | |
194 | f->close_section(); | |
195 | } | |
196 | ||
197 | virtual void dump_item_fields(const Item &qi, Formatter *f) { | |
198 | CrushTreeDumper::dump_item_fields(crush, qi, f); | |
199 | } | |
200 | ||
201 | virtual void dump_bucket_children(const Item &qi, Formatter *f) { | |
202 | CrushTreeDumper::dump_bucket_children(crush, qi, f); | |
203 | } | |
204 | }; | |
205 | ||
206 | } | |
207 | ||
208 | #endif |