]> git.proxmox.com Git - ceph.git/blob - ceph/src/crush/CrushTreeDumper.h
40691c697d3a75487eb0414ded0470b597b5ec4c
[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
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_) {
67 crush->find_roots(roots);
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
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
100 bool next(Item &qi) {
101 if (empty()) {
102 while (root != roots.end() && !should_dump(*root))
103 ++root;
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);
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 }
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);
153 if (qi.is_bucket()) {
154 int type = crush->get_bucket_type(qi.id);
155 f->dump_string("name", crush->get_item_name(qi.id));
156 f->dump_string("type", crush->get_type_name(type));
157 f->dump_int("type_id", type);
158 } else {
159 f->dump_stream("name") << "osd." << qi.id;
160 f->dump_string("type", crush->get_type_name(0));
161 f->dump_int("type_id", 0);
162 f->dump_float("crush_weight", qi.weight);
163 f->dump_unsigned("depth", qi.depth);
164 }
165 }
166
167 inline void dump_bucket_children(const CrushWrapper *crush,
168 const Item &qi, Formatter *f) {
169 if (!qi.is_bucket())
170 return;
171
172 f->open_array_section("children");
173 for (list<int>::const_iterator i = qi.children.begin();
174 i != qi.children.end();
175 ++i) {
176 f->dump_int("child", *i);
177 }
178 f->close_section();
179 }
180
181 class FormattingDumper : public Dumper<Formatter> {
182 public:
183 explicit FormattingDumper(const CrushWrapper *crush) : Dumper<Formatter>(crush) {}
184
185 protected:
186 void dump_item(const Item &qi, Formatter *f) override {
187 f->open_object_section("item");
188 dump_item_fields(qi, f);
189 dump_bucket_children(qi, f);
190 f->close_section();
191 }
192
193 virtual void dump_item_fields(const Item &qi, Formatter *f) {
194 CrushTreeDumper::dump_item_fields(crush, qi, f);
195 }
196
197 virtual void dump_bucket_children(const Item &qi, Formatter *f) {
198 CrushTreeDumper::dump_bucket_children(crush, qi, f);
199 }
200 };
201
202 }
203
204 #endif