1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph distributed storage system
6 * Copyright (C) 2015 Mirantis Inc
8 * Author: Mykola Golub <mgolub@mirantis.com>
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.
17 #ifndef CRUSH_TREE_DUMPER_H
18 #define CRUSH_TREE_DUMPER_H
20 #include "CrushWrapper.h"
21 #include "include/stringify.h"
25 * A helper class and functions to dump a crush tree.
29 * class SimpleDumper : public CrushTreeDumper::Dumper<ostream> {
31 * SimpleDumper(const CrushWrapper *crush) :
32 * CrushTreeDumper::Dumper<ostream>(crush) {}
34 * virtual void dump_item(const CrushTreeDumper::Item &qi, ostream *out) {
36 * for (int k = 0; k < qi.depth; k++)
39 * *out << crush->get_item_name(qi.id)
41 * *out << "osd." << qi.id;
46 * SimpleDumper(crush).dump(out);
50 namespace CrushTreeDumper {
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) {}
62 bool is_bucket() const { return id < 0; }
66 class Dumper : public list<Item> {
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);
74 explicit Dumper(const CrushWrapper *crush_,
75 const name_map_t& weight_set_names_,
77 : crush(crush_), weight_set_names(weight_set_names_) {
79 crush->find_roots(&roots);
81 crush->find_nonshadow_roots(&roots);
88 virtual void reset() {
94 virtual bool should_dump_leaf(int i) const {
97 virtual bool should_dump_empty_bucket() const {
101 bool should_dump(int id) {
103 return should_dump_leaf(id);
104 if (should_dump_empty_bucket())
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);
115 bool next(Item &qi) {
117 while (root != roots.end() && !should_dump(*root))
119 if (root == roots.end())
121 push_back(Item(*root, 0, 0, crush->get_bucket_weightf(*root)));
127 touched.insert(qi.id);
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)) {
138 const char *c = crush->get_item_class(id);
139 sort_by = c ? c : "";
142 snprintf(nn, sizeof(nn), "osd.%08d", id);
146 sort_by += crush->get_item_name(id);
148 sorted[sort_by] = make_pair(
149 id, crush->get_bucket_item_weightf(qi.id, k));
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,
168 bool is_touched(int id) const { return touched.count(id) > 0; }
171 virtual void dump_item(const Item &qi, F *f) = 0;
174 const CrushWrapper *crush;
175 const name_map_t &weight_set_names;
180 set<int>::iterator root;
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);
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);
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);
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);
209 bidx < (int)cmap.size &&
210 cmap.args[bidx].weight_set &&
211 cmap.args[bidx].weight_set_size >= 1) {
214 bpos < (int)cmap.args[bidx].weight_set[0].size &&
215 b->items[bpos] != qi.id;
218 if (p.first == CrushWrapper::DEFAULT_CHOOSE_ARGS) {
221 auto q = weight_set_names.find(p.first);
222 name = q != weight_set_names.end() ? q->second :
225 f->open_array_section(name.c_str());
226 for (unsigned opos = 0;
227 opos < cmap.args[bidx].weight_set_size;
229 float w = (float)cmap.args[bidx].weight_set[opos].weights[bpos] /
231 f->dump_float("weight", w);
240 inline void dump_bucket_children(const CrushWrapper *crush,
241 const Item &qi, Formatter *f) {
245 f->open_array_section("children");
246 for (list<int>::const_iterator i = qi.children.begin();
247 i != qi.children.end();
249 f->dump_int("child", *i);
254 class FormattingDumper : public Dumper<Formatter> {
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,
262 : Dumper<Formatter>(crush, weight_set_names, show_shadow) {}
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);
272 virtual void dump_item_fields(const Item &qi, Formatter *f) {
273 CrushTreeDumper::dump_item_fields(crush, weight_set_names, qi, f);
276 virtual void dump_bucket_children(const Item &qi, Formatter *f) {
277 CrushTreeDumper::dump_bucket_children(crush, qi, f);