]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mon/ConfigMap: parsed cluster-wide config
authorSage Weil <sage@redhat.com>
Sat, 2 Dec 2017 04:41:46 +0000 (22:41 -0600)
committerSage Weil <sage@redhat.com>
Tue, 6 Mar 2018 20:44:48 +0000 (14:44 -0600)
Breaks down options by global, type, daemon, and can filter options by
crush location.

Signed-off-by: Sage Weil <sage@redhat.com>
src/mon/CMakeLists.txt
src/mon/ConfigMap.cc [new file with mode: 0644]
src/mon/ConfigMap.h [new file with mode: 0644]

index ebbc863c28b81acc90c8d1051a6b72ea4ec915cd..02b3ee409e0541c375f907e4437918b46f933c95 100644 (file)
@@ -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 (file)
index 0000000..585d4c4
--- /dev/null
@@ -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<std::string,std::string>& crush_location,
+  const CrushWrapper *crush,
+  const std::string& device_class,
+  std::map<std::string,std::string> *out)
+{
+  // global, then by type, then by full name.
+  vector<Section*> 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 (file)
index 0000000..c05abba
--- /dev/null
@@ -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 <map>
+#include <ostream>
+#include <string>
+
+#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<std::string,MaskedOption> options;
+
+  void clear() {
+    options.clear();
+  }
+  void dump(Formatter *f) const;
+};
+
+struct ConfigMap {
+  Section global;
+  std::map<std::string,Section> by_type;
+  std::map<std::string,Section> 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<std::string,std::string>& crush_location,
+    const CrushWrapper *crush,
+    const std::string& device_class,
+    std::map<std::string,std::string> *out);
+};