diff options
Diffstat (limited to 'src/crush/CrushTreeDumper.h')
-rw-r--r-- | src/crush/CrushTreeDumper.h | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/src/crush/CrushTreeDumper.h b/src/crush/CrushTreeDumper.h new file mode 100644 index 000000000..a10c0f2c1 --- /dev/null +++ b/src/crush/CrushTreeDumper.h @@ -0,0 +1,291 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph distributed storage system + * + * Copyright (C) 2015 Mirantis Inc + * + * Author: Mykola Golub <mgolub@mirantis.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + */ + +#ifndef CRUSH_TREE_DUMPER_H +#define CRUSH_TREE_DUMPER_H + +#include "CrushWrapper.h" +#include "include/stringify.h" + +/** + * CrushTreeDumper: + * A helper class and functions to dump a crush tree. + * + * Example: + * + * class SimpleDumper : public CrushTreeDumper::Dumper<ostream> { + * public: + * SimpleDumper(const CrushWrapper *crush) : + * CrushTreeDumper::Dumper<ostream>(crush) {} + * protected: + * virtual void dump_item(const CrushTreeDumper::Item &qi, ostream *out) { + * *out << qi.id; + * for (int k = 0; k < qi.depth; k++) + * *out << "-"; + * if (qi.is_bucket()) + * *out << crush->get_item_name(qi.id) + * else + * *out << "osd." << qi.id; + * *out << "\n"; + * } + * }; + * + * SimpleDumper(crush).dump(out); + * + */ + +namespace CrushTreeDumper { + + struct Item { + int id; + int parent; + int depth; + float weight; + std::list<int> children; + + Item() : id(0), parent(0), depth(0), weight(0) {} + Item(int i, int p, int d, float w) : id(i), parent(p), depth(d), weight(w) {} + + bool is_bucket() const { return id < 0; } + }; + + template <typename F> + class Dumper : public std::list<Item> { + public: + explicit Dumper(const CrushWrapper *crush_, + const name_map_t& weight_set_names_) + : crush(crush_), weight_set_names(weight_set_names_) { + crush->find_nonshadow_roots(&roots); + root = roots.begin(); + } + explicit Dumper(const CrushWrapper *crush_, + const name_map_t& weight_set_names_, + bool show_shadow) + : crush(crush_), weight_set_names(weight_set_names_) { + if (show_shadow) { + crush->find_roots(&roots); + } else { + crush->find_nonshadow_roots(&roots); + } + root = roots.begin(); + } + + virtual ~Dumper() {} + + virtual void reset() { + root = roots.begin(); + touched.clear(); + clear(); + } + + virtual bool should_dump_leaf(int i) const { + return true; + } + virtual bool should_dump_empty_bucket() const { + return true; + } + + bool should_dump(int id) { + if (id >= 0) + return should_dump_leaf(id); + if (should_dump_empty_bucket()) + return true; + int s = crush->get_bucket_size(id); + for (int k = s - 1; k >= 0; k--) { + int c = crush->get_bucket_item(id, k); + if (should_dump(c)) + return true; + } + return false; + } + + bool next(Item &qi) { + if (empty()) { + while (root != roots.end() && !should_dump(*root)) + ++root; + if (root == roots.end()) + return false; + push_back(Item(*root, 0, 0, crush->get_bucket_weightf(*root))); + ++root; + } + + qi = front(); + pop_front(); + touched.insert(qi.id); + + if (qi.is_bucket()) { + // queue bucket contents, sorted by (class, name) + int s = crush->get_bucket_size(qi.id); + std::map<std::string, std::pair<int,float>> sorted; + for (int k = s - 1; k >= 0; k--) { + int id = crush->get_bucket_item(qi.id, k); + if (should_dump(id)) { + std::string sort_by; + if (id >= 0) { + const char *c = crush->get_item_class(id); + sort_by = c ? c : ""; + sort_by += "_"; + char nn[80]; + snprintf(nn, sizeof(nn), "osd.%08d", id); + sort_by += nn; + } else { + sort_by = "_"; + sort_by += crush->get_item_name(id); + } + sorted[sort_by] = std::make_pair( + id, crush->get_bucket_item_weightf(qi.id, k)); + } + } + for (auto p = sorted.rbegin(); p != sorted.rend(); ++p) { + qi.children.push_back(p->second.first); + push_front(Item(p->second.first, qi.id, qi.depth + 1, + p->second.second)); + } + } + return true; + } + + void dump(F *f) { + reset(); + Item qi; + while (next(qi)) + dump_item(qi, f); + } + + bool is_touched(int id) const { return touched.count(id) > 0; } + + void set_root(const std::string& bucket) { + roots.clear(); + if (crush->name_exists(bucket)) { + int i = crush->get_item_id(bucket); + roots.insert(i); + } + } + + protected: + virtual void dump_item(const Item &qi, F *f) = 0; + + protected: + const CrushWrapper *crush; + const name_map_t &weight_set_names; + + private: + std::set<int> touched; + std::set<int> roots; + std::set<int>::iterator root; + }; + + inline void dump_item_fields(const CrushWrapper *crush, + const name_map_t& weight_set_names, + const Item &qi, ceph::Formatter *f) { + f->dump_int("id", qi.id); + const char *c = crush->get_item_class(qi.id); + if (c) + f->dump_string("device_class", c); + if (qi.is_bucket()) { + int type = crush->get_bucket_type(qi.id); + f->dump_string("name", crush->get_item_name(qi.id)); + f->dump_string("type", crush->get_type_name(type)); + f->dump_int("type_id", type); + } else { + f->dump_stream("name") << "osd." << qi.id; + f->dump_string("type", crush->get_type_name(0)); + f->dump_int("type_id", 0); + f->dump_float("crush_weight", qi.weight); + f->dump_unsigned("depth", qi.depth); + } + if (qi.parent < 0) { + f->open_object_section("pool_weights"); + for (auto& p : crush->choose_args) { + const crush_choose_arg_map& cmap = p.second; + int bidx = -1 - qi.parent; + const crush_bucket *b = crush->get_bucket(qi.parent); + if (b && + bidx < (int)cmap.size && + cmap.args[bidx].weight_set && + cmap.args[bidx].weight_set_positions >= 1) { + int bpos; + for (bpos = 0; + bpos < (int)cmap.args[bidx].weight_set[0].size && + b->items[bpos] != qi.id; + ++bpos) ; + std::string name; + if (p.first == CrushWrapper::DEFAULT_CHOOSE_ARGS) { + name = "(compat)"; + } else { + auto q = weight_set_names.find(p.first); + name = q != weight_set_names.end() ? q->second : + stringify(p.first); + } + f->open_array_section(name.c_str()); + for (unsigned opos = 0; + opos < cmap.args[bidx].weight_set_positions; + ++opos) { + float w = (float)cmap.args[bidx].weight_set[opos].weights[bpos] / + (float)0x10000; + f->dump_float("weight", w); + } + f->close_section(); + } + } + f->close_section(); + } + } + + inline void dump_bucket_children(const CrushWrapper *crush, + const Item &qi, ceph::Formatter *f) { + if (!qi.is_bucket()) + return; + + f->open_array_section("children"); + for (std::list<int>::const_iterator i = qi.children.begin(); + i != qi.children.end(); + ++i) { + f->dump_int("child", *i); + } + f->close_section(); + } + + class FormattingDumper : public Dumper<ceph::Formatter> { + public: + explicit FormattingDumper(const CrushWrapper *crush, + const name_map_t& weight_set_names) + : Dumper<ceph::Formatter>(crush, weight_set_names) {} + explicit FormattingDumper(const CrushWrapper *crush, + const name_map_t& weight_set_names, + bool show_shadow) + : Dumper<ceph::Formatter>(crush, weight_set_names, show_shadow) {} + + protected: + void dump_item(const Item &qi, ceph::Formatter *f) override { + f->open_object_section("item"); + dump_item_fields(qi, f); + dump_bucket_children(qi, f); + f->close_section(); + } + + virtual void dump_item_fields(const Item &qi, ceph::Formatter *f) { + CrushTreeDumper::dump_item_fields(crush, weight_set_names, qi, f); + } + + virtual void dump_bucket_children(const Item &qi, ceph::Formatter *f) { + CrushTreeDumper::dump_bucket_children(crush, qi, f); + } + }; + +} + +#endif |