From 43a9d164d646d655ae18e9b8e12ecc63a4a54f5f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 1 Dec 2017 22:41:46 -0600 Subject: [PATCH] mon/ConfigMap: parsed cluster-wide config Breaks down options by global, type, daemon, and can filter options by crush location. Signed-off-by: Sage Weil --- src/mon/CMakeLists.txt | 1 + src/mon/ConfigMap.cc | 119 +++++++++++++++++++++++++++++++++++++++++ src/mon/ConfigMap.h | 75 ++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 src/mon/ConfigMap.cc create mode 100644 src/mon/ConfigMap.h diff --git a/src/mon/CMakeLists.txt b/src/mon/CMakeLists.txt index ebbc863c28b..02b3ee409e0 100644 --- a/src/mon/CMakeLists.txt +++ b/src/mon/CMakeLists.txt @@ -14,6 +14,7 @@ set(lib_mon_srcs MonmapMonitor.cc LogMonitor.cc AuthMonitor.cc + ConfigMap.cc Elector.cc HealthMonitor.cc PGMap.cc diff --git a/src/mon/ConfigMap.cc b/src/mon/ConfigMap.cc new file mode 100644 index 00000000000..585d4c43c61 --- /dev/null +++ b/src/mon/ConfigMap.cc @@ -0,0 +1,119 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "ConfigMap.h" +#include "crush/CrushWrapper.h" + +int MaskedOption::get_precision(const CrushWrapper *crush) +{ + // 0 = most precise + if (location_type.size()) { + int r = crush->get_type_id(location_type); + if (r >= 0) { + return r; + } + // bad type name, ignore it + } + int num_types = crush->get_num_type_names(); + if (device_class.size()) { + return num_types; + } + return num_types + 1; +} + +void MaskedOption::dump(Formatter *f) const +{ + f->dump_string("name", opt.name); + f->dump_string("value", raw_value); + if (location_type.size()) { + f->dump_string("location_type", location_type); + f->dump_string("location_value", location_value); + } + if (device_class.size()) { + f->dump_string("device_class", device_class); + } +} + +ostream& operator<<(ostream& out, const MaskedOption& o) +{ + out << o.opt.name; + if (o.location_type.size()) { + out << "@" << o.location_type << '=' << o.location_value; + } + if (o.device_class.size()) { + out << "@class=" << o.device_class; + } + return out; +} + +// ---------- + +void Section::dump(Formatter *f) const +{ + for (auto& i : options) { + f->dump_object(i.first.c_str(), i.second); + } +} + +// ------------ + +void ConfigMap::dump(Formatter *f) const +{ + f->dump_object("global", global); + f->open_object_section("by_type"); + for (auto& i : by_type) { + f->dump_object(i.first.c_str(), i.second); + } + f->close_section(); + f->open_object_section("by_id"); + for (auto& i : by_id) { + f->dump_object(i.first.c_str(), i.second); + } + f->close_section(); +} + +void ConfigMap::generate_entity_map( + const EntityName& name, + const map& crush_location, + const CrushWrapper *crush, + const std::string& device_class, + std::map *out) +{ + // global, then by type, then by full name. + vector sections = { &global }; + auto p = by_type.find(name.get_type_name()); + if (p != by_type.end()) { + sections.push_back(&p->second); + } + auto q = by_id.find(name.to_str()); + if (q != by_id.end()) { + sections.push_back(&q->second); + } + MaskedOption *prev = nullptr; + for (auto s : sections) { + for (auto& i : s->options) { + auto& o = i.second; + // match against crush location, class + if (o.device_class.size() && + o.device_class != device_class) { + continue; + } + if (o.location_type.size()) { + auto p = crush_location.find(o.location_type); + if (p == crush_location.end() || + p->second != o.location_value) { + continue; + } + } + if (prev && prev->opt.name != i.first) { + prev = nullptr; + } + if (prev && + prev->get_precision(crush) < o.get_precision(crush)) { + continue; + } + (*out)[i.first] = o.raw_value; + prev = &o; + } + } +} diff --git a/src/mon/ConfigMap.h b/src/mon/ConfigMap.h new file mode 100644 index 00000000000..c05abba86ef --- /dev/null +++ b/src/mon/ConfigMap.h @@ -0,0 +1,75 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#pragma once + +#include +#include +#include + +#include "common/options.h" +#include "common/entity_name.h" + +class CrushWrapper; + +// the precedence is thus: +// +// global +// crush location (coarse to fine, ordered by type id) +// daemon type (e.g., osd) +// device class (osd only) +// crush location (coarse to fine, ordered by type id) +// daemon name (e.g., mds.foo) +// +// Note that this means that if we have +// +// config/host:foo/a = 1 +// config/osd/rack:foo/a = 2 +// +// then we get a = 2. The osd-level config wins, even though rack +// is less precise than host, because the crush limiters are only +// resolved within a section (global, per-daemon, per-instance). + +struct MaskedOption { + string raw_value; ///< raw, unparsed, unvalidated value + Option opt; ///< the option + std::string location_type, location_value; ///< matches crush_location + std::string device_class; ///< matches device class + + MaskedOption(const Option& o) : opt(o) {} + + /// return a precision metric (smaller is more precise) + int get_precision(const CrushWrapper *crush); + + friend ostream& operator<<(ostream& out, const MaskedOption& o); + + void dump(Formatter *f) const; +}; + +struct Section { + std::multimap options; + + void clear() { + options.clear(); + } + void dump(Formatter *f) const; +}; + +struct ConfigMap { + Section global; + std::map by_type; + std::map by_id; + + void clear() { + global.clear(); + by_type.clear(); + by_id.clear(); + } + void dump(Formatter *f) const; + void generate_entity_map( + const EntityName& name, + const map& crush_location, + const CrushWrapper *crush, + const std::string& device_class, + std::map *out); +}; -- 2.39.5