]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/common/config.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / common / config.cc
index b6feaba4b1f177080f1251a742b4d5b13cdeb774..c8101587b7190527088481a4b98ab1037738a308 100644 (file)
@@ -12,8 +12,7 @@
  *
  */
 
-#include <boost/type_traits.hpp>
-
+#include <filesystem>
 #include "common/ceph_argparse.h"
 #include "common/common_init.h"
 #include "common/config.h"
 // set set_mon_vals()
 #define dout_subsys ceph_subsys_monc
 
+namespace fs = std::filesystem;
+
+using std::cerr;
+using std::cout;
 using std::map;
+using std::less;
 using std::list;
+using std::ostream;
 using std::ostringstream;
 using std::pair;
 using std::string;
+using std::string_view;
+using std::vector;
 
-static const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config, /etc/ceph/$cluster.conf, $home/.ceph/$cluster.conf, $cluster.conf"
+using ceph::bufferlist;
+using ceph::decode;
+using ceph::encode;
+using ceph::Formatter;
+
+static const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config,/etc/ceph/$cluster.conf,$home/.ceph/$cluster.conf,$cluster.conf"
 #if defined(__FreeBSD__)
-    ", /usr/local/etc/ceph/$cluster.conf"
+    ",/usr/local/etc/ceph/$cluster.conf"
+#elif defined(_WIN32)
+    ",$programdata/ceph/$cluster.conf"
 #endif
     ;
 
@@ -70,7 +84,7 @@ int ceph_resolve_file_search(const std::string& filename_list,
                             std::string& result)
 {
   list<string> ls;
-  get_str_list(filename_list, ls);
+  get_str_list(filename_list, ";,", ls);
 
   int ret = -ENOENT;
   list<string>::iterator iter;
@@ -90,7 +104,7 @@ int ceph_resolve_file_search(const std::string& filename_list,
 
 static int conf_stringify(const Option::value_t& v, string *out)
 {
-  if (boost::get<boost::blank>(&v)) {
+  if (v == Option::value_t{}) {
     return -ENOENT;
   }
   *out = Option::to_str(v);
@@ -111,9 +125,7 @@ md_config_t::md_config_t(ConfigValues& values,
                 << std::endl;
       ceph_abort();
     }
-    schema.emplace(std::piecewise_construct,
-                  std::forward_as_tuple(i.name),
-                  std::forward_as_tuple(i));
+    schema.emplace(i.name, i);
   }
 
   // Define the debug_* options as well.
@@ -166,9 +178,9 @@ md_config_t::md_config_t(ConfigValues& values,
   // members if present.
   legacy_values = {
 #define OPTION(name, type) \
-    {std::string(STRINGIFY(name)), &ConfigValues::name},
+    {STRINGIFY(name), &ConfigValues::name},
 #define SAFE_OPTION(name, type) OPTION(name, type)
-#include "common/legacy_config_opts.h"
+#include "options/legacy_config_opts.h"
 #undef OPTION
 #undef SAFE_OPTION
   };
@@ -179,7 +191,7 @@ md_config_t::md_config_t(ConfigValues& values,
   for (const auto &i : schema) {
     const Option &opt = i.second;
     if (opt.type == Option::TYPE_STR) {
-      bool has_daemon_default = !boost::get<boost::blank>(&opt.daemon_value);
+      bool has_daemon_default = (opt.daemon_value != Option::value_t{});
       Option::value_t default_val;
       if (is_daemon && has_daemon_default) {
        default_val = opt.daemon_value;
@@ -188,7 +200,7 @@ md_config_t::md_config_t(ConfigValues& values,
       }
       // We call pre_validate as a sanity check, but also to get any
       // side effect (value modification) from the validator.
-      std::string *def_str = boost::get<std::string>(&default_val);
+      auto* def_str = std::get_if<std::string>(&default_val);
       std::string val = *def_str;
       std::string err;
       if (opt.pre_validate(&val, &err) != 0) {
@@ -242,7 +254,7 @@ void md_config_t::validate_schema()
   }
 }
 
-const Option *md_config_t::find_option(const string& name) const
+const Option *md_config_t::find_option(const std::string_view name) const
 {
   auto p = schema.find(name);
   if (p != schema.end()) {
@@ -253,7 +265,7 @@ const Option *md_config_t::find_option(const string& name) const
 
 void md_config_t::set_val_default(ConfigValues& values,
                                  const ConfigTracker& tracker,
-                                 const string& name, const std::string& val)
+                                 const string_view name, const std::string& val)
 {
   const Option *o = find_option(name);
   ceph_assert(o);
@@ -265,7 +277,7 @@ void md_config_t::set_val_default(ConfigValues& values,
 int md_config_t::set_mon_vals(CephContext *cct,
     ConfigValues& values,
     const ConfigTracker& tracker,
-    const map<string,string>& kv,
+    const map<string,string,less<>>& kv,
     config_callback config_cb)
 {
   ignored_mon_values.clear();
@@ -295,8 +307,8 @@ int md_config_t::set_mon_vals(CephContext *cct,
     std::string err;
     int r = _set_val(values, tracker, i.second, *o, CONF_MON, &err);
     if (r < 0) {
-      lderr(cct) << __func__ << " failed to set " << i.first << " = "
-                << i.second << ": " << err << dendl;
+      ldout(cct, 4) << __func__ << " failed to set " << i.first << " = "
+                   << i.second << ": " << err << dendl;
       ignored_mon_values.emplace(i);
     } else if (r == ConfigValues::SET_NO_CHANGE ||
               r == ConfigValues::SET_NO_EFFECT) {
@@ -320,6 +332,11 @@ int md_config_t::set_mon_vals(CephContext *cct,
                  << " cleared (was " << Option::to_str(config->second) << ")"
                  << dendl;
     values.rm_val(name, CONF_MON);
+    // if this is a debug option, it needs to propagate to teh subsys;
+    // this isn't covered by update_legacy_vals() below.  similarly,
+    // we want to trigger a config notification for these items.
+    const Option *o = find_option(name);
+    _refresh(values, *o);
   });
   values_bl.clear();
   update_legacy_vals(values);
@@ -332,125 +349,125 @@ int md_config_t::parse_config_files(ConfigValues& values,
                                    std::ostream *warnings,
                                    int flags)
 {
-
   if (safe_to_start_threads)
     return -ENOSYS;
 
-  if (!values.cluster.size() && !conf_files_str) {
-    /*
-     * set the cluster name to 'ceph' when neither cluster name nor
-     * configuration file are specified.
-     */
-    values.cluster = "ceph";
-  }
-
-  if (!conf_files_str) {
-    const char *c = getenv("CEPH_CONF");
-    if (c) {
-      conf_files_str = c;
-    }
-    else {
-      if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)
-       return 0;
-      conf_files_str = CEPH_CONF_FILE_DEFAULT;
-    }
-  }
-
-  std::list<std::string> conf_files;
-  get_str_list(conf_files_str, conf_files);
-  auto p = conf_files.begin();
-  while (p != conf_files.end()) {
-    string &s = *p;
-    if (s.find("$data_dir") != string::npos &&
-       data_dir_option.empty()) {
-      // useless $data_dir item, skip
-      p = conf_files.erase(p);
-    } else {
-      early_expand_meta(values, s, warnings);
-      ++p;
-    }
+  if (values.cluster.empty() && !conf_files_str) {
+    values.cluster = get_cluster_name(nullptr);
   }
-
   // open new conf
-  list<string>::const_iterator c;
-  for (c = conf_files.begin(); c != conf_files.end(); ++c) {
-    cf.clear();
-    string fn = *c;
-
-    int ret = cf.parse_file(fn.c_str(), &parse_errors, warnings);
-    if (ret == 0)
+  for (auto& fn : get_conffile_paths(values, conf_files_str, warnings, flags)) {
+    bufferlist bl;
+    std::string error;
+    if (bl.read_file(fn.c_str(), &error)) {
+      parse_error = error;
+      continue;
+    }
+    ostringstream oss;
+    int ret = parse_buffer(values, tracker, bl.c_str(), bl.length(), &oss);
+    if (ret == 0) {
+      parse_error.clear();
+      conf_path = fn;
       break;
-    else if (ret != -ENOENT)
+    }
+    parse_error = oss.str();
+    if (ret != -ENOENT) {
       return ret;
+    }
   }
   // it must have been all ENOENTs, that's the only way we got here
-  if (c == conf_files.end())
+  if (conf_path.empty()) {
     return -ENOENT;
-
-  if (values.cluster.size() == 0) {
-    /*
-     * If cluster name is not set yet, use the prefix of the
-     * basename of configuration file as cluster name.
-     */
-    auto start = c->rfind('/') + 1;
-    auto end = c->find(".conf", start);
-    if (end == c->npos) {
-        /*
-         * If the configuration file does not follow $cluster.conf
-         * convention, we do the last try and assign the cluster to
-         * 'ceph'.
-         */
-        values.cluster = "ceph";
-    } else {
-      values.cluster = c->substr(start, end - start);
-    }
   }
+  if (values.cluster.empty()) {
+    values.cluster = get_cluster_name(conf_path.c_str());
+  }
+  update_legacy_vals(values);
+  return 0;
+}
 
-  std::vector <std::string> my_sections;
-  _get_my_sections(values, my_sections);
+int
+md_config_t::parse_buffer(ConfigValues& values,
+                         const ConfigTracker& tracker,
+                         const char* buf, size_t len,
+                         std::ostream* warnings)
+{
+  if (!cf.parse_buffer(string_view{buf, len}, warnings)) {
+    return -EINVAL;
+  }
+  const auto my_sections = get_my_sections(values);
   for (const auto &i : schema) {
     const auto &opt = i.second;
     std::string val;
-    int ret = _get_val_from_conf_file(my_sections, opt.name, val);
-    if (ret == 0) {
-      std::string error_message;
-      int r = _set_val(values, tracker, val, opt, CONF_FILE, &error_message);
-      if (warnings != nullptr && (r < 0 || !error_message.empty())) {
-        *warnings << "parse error setting '" << opt.name << "' to '" << val
-                  << "'";
+    if (_get_val_from_conf_file(my_sections, opt.name, val)) {
+      continue;
+    }
+    std::string error_message;
+    if (_set_val(values, tracker, val, opt, CONF_FILE, &error_message) < 0) {
+      if (warnings != nullptr) {
+        *warnings << "parse error setting " << std::quoted(opt.name)
+                  << " to " << std::quoted(val);
         if (!error_message.empty()) {
           *warnings << " (" << error_message << ")";
         }
-        *warnings << std::endl;
+        *warnings << '\n';
       }
     }
   }
+  cf.check_old_style_section_names({"mds", "mon", "osd"}, cerr);
+  return 0;
+}
 
-  // Warn about section names that look like old-style section names
-  std::deque < std::string > old_style_section_names;
-  for (ConfFile::const_section_iter_t s = cf.sections_begin();
-       s != cf.sections_end(); ++s) {
-    const string &str(s->first);
-    if (((str.find("mds") == 0) || (str.find("mon") == 0) ||
-        (str.find("osd") == 0)) && (str.size() > 3) && (str[3] != '.')) {
-      old_style_section_names.push_back(str);
+std::list<std::string>
+md_config_t::get_conffile_paths(const ConfigValues& values,
+                               const char *conf_files_str,
+                               std::ostream *warnings,
+                               int flags) const
+{
+  if (!conf_files_str) {
+    const char *c = getenv("CEPH_CONF");
+    if (c) {
+      conf_files_str = c;
+    } else {
+      if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE)
+       return {};
+      conf_files_str = CEPH_CONF_FILE_DEFAULT;
     }
   }
-  if (!old_style_section_names.empty()) {
-    ostringstream oss;
-    cerr << "ERROR! old-style section name(s) found: ";
-    string sep;
-    for (std::deque < std::string >::const_iterator os = old_style_section_names.begin();
-        os != old_style_section_names.end(); ++os) {
-      cerr << sep << *os;
-      sep = ", ";
+
+  std::list<std::string> paths;
+  get_str_list(conf_files_str, ";,", paths);
+  for (auto i = paths.begin(); i != paths.end(); ) {
+    string& path = *i;
+    if (path.find("$data_dir") != path.npos &&
+       data_dir_option.empty()) {
+      // useless $data_dir item, skip
+      i = paths.erase(i);
+    } else {
+      early_expand_meta(values, path, warnings);
+      ++i;
     }
-    cerr << ". Please use the new style section names that include a period.";
   }
+  return paths;
+}
 
-  update_legacy_vals(values);
-
-  return 0;
+std::string md_config_t::get_cluster_name(const char* conffile)
+{
+  if (conffile) {
+    // If cluster name is not set yet, use the prefix of the
+    // basename of configuration file as cluster name.
+    if (fs::path path{conffile}; path.extension() == ".conf") {
+      return path.stem().string();
+    } else {
+      // If the configuration file does not follow $cluster.conf
+      // convention, we do the last try and assign the cluster to
+      // 'ceph'.
+      return "ceph";
+    }
+  } else {
+    // set the cluster name to 'ceph' when configuration file is not specified.
+    return "ceph";
+  }
 }
 
 void md_config_t::parse_env(unsigned entity_type,
@@ -463,11 +480,11 @@ void md_config_t::parse_env(unsigned entity_type,
   if (!args_var) {
     args_var = "CEPH_ARGS";
   }
-  if (getenv("CEPH_KEYRING")) {
-    _set_val(values, tracker, getenv("CEPH_KEYRING"), *find_option("keyring"),
-            CONF_ENV, nullptr);
+  if (auto s = getenv("CEPH_KEYRING"); s) {
+    string err;
+    _set_val(values, tracker, s, *find_option("keyring"), CONF_ENV, &err);
   }
-  if (const char *dir = getenv("CEPH_LIB")) {
+  if (auto dir = getenv("CEPH_LIB"); dir) {
     for (auto name : { "erasure_code_dir", "plugin_dir", "osd_class_dir" }) {
     std::string err;
       const Option *o = find_option(name);
@@ -475,19 +492,93 @@ void md_config_t::parse_env(unsigned entity_type,
       _set_val(values, tracker, dir, *o, CONF_ENV, &err);
     }
   }
-  const char *pod_req = getenv("POD_MEMORY_REQUEST");
-  if (pod_req) {
-    uint64_t v = atoll(pod_req);
+
+  // Apply pod memory limits:
+  //
+  // There are two types of resource requests: `limits` and `requests`.
+  //
+  // - Requests: Used by the K8s scheduler to determine on which nodes to
+  //   schedule the pods. This helps spread the pods to different nodes. This
+  //   value should be conservative in order to make sure all the pods are
+  //   schedulable. This corresponds to POD_MEMORY_REQUEST (set by the Rook
+  //   CRD) and is the target memory utilization we try to maintain for daemons
+  //   that respect it.
+  //
+  //   If POD_MEMORY_REQUEST is present, we use it as the target.
+  //
+  // - Limits: At runtime, the container runtime (and Linux) will use the
+  //   limits to see if the pod is using too many resources. In that case, the
+  //   pod will be killed/restarted automatically if the pod goes over the limit.
+  //   This should be higher than what is specified for requests (potentially
+  //   much higher). This corresponds to the cgroup memory limit that will
+  //   trigger the Linux OOM killer.
+  //
+  //   If POD_MEMORY_LIMIT is present, we use it as the /default/ value for
+  //   the target, which means it will only apply if the *_memory_target option
+  //   isn't set via some other path (e.g., POD_MEMORY_REQUEST, or the cluster
+  //   config, or whatever.)
+  //
+  // Here are the documented best practices:
+  //   https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/#motivation-for-cpu-requests-and-limits
+  //
+  // When the operator creates the CephCluster CR, it will need to generate the
+  // desired requests and limits. As long as we are conservative in our choice
+  // for requests and generous with the limits we should be in a good place to
+  // get started.
+  //
+  // The support in Rook is already there for applying the limits as seen in
+  // these links.
+  //
+  // Rook docs on the resource requests and limits:
+  //   https://rook.io/docs/rook/v1.0/ceph-cluster-crd.html#cluster-wide-resources-configuration-settings
+  // Example CR settings:
+  //   https://github.com/rook/rook/blob/6d2ef936698593036185aabcb00d1d74f9c7bfc1/cluster/examples/kubernetes/ceph/cluster.yaml#L90
+  //
+  uint64_t pod_limit = 0, pod_request = 0;
+  if (auto pod_lim = getenv("POD_MEMORY_LIMIT"); pod_lim) {
+    string err;
+    uint64_t v = atoll(pod_lim);
     if (v) {
       switch (entity_type) {
       case CEPH_ENTITY_TYPE_OSD:
-       _set_val(values, tracker, stringify(v),
-                *find_option("osd_memory_target"),
-                CONF_ENV, nullptr);
-       break;
+        {
+         double cgroup_ratio = get_val<double>(
+           values, "osd_memory_target_cgroup_limit_ratio");
+         if (cgroup_ratio > 0.0) {
+           pod_limit = v * cgroup_ratio;
+           // set osd_memory_target *default* based on cgroup limit, so that
+           // it can be overridden by any explicit settings elsewhere.
+           set_val_default(values, tracker,
+                           "osd_memory_target", stringify(pod_limit));
+         }
+       }
       }
     }
   }
+  if (auto pod_req = getenv("POD_MEMORY_REQUEST"); pod_req) {
+    if (uint64_t v = atoll(pod_req); v) {
+      pod_request = v;
+    }
+  }
+  if (pod_request && pod_limit) {
+    // If both LIMIT and REQUEST are set, ensure that we use the
+    // min of request and limit*ratio.  This is important
+    // because k8s set set LIMIT == REQUEST if only LIMIT is
+    // specified, and we want to apply the ratio in that case,
+    // even though REQUEST is present.
+    pod_request = std::min<uint64_t>(pod_request, pod_limit);
+  }
+  if (pod_request) {
+    string err;
+    switch (entity_type) {
+    case CEPH_ENTITY_TYPE_OSD:
+      _set_val(values, tracker, stringify(pod_request),
+              *find_option("osd_memory_target"),
+              CONF_ENV, &err);
+      break;
+    }
+  }
+
   if (getenv(args_var)) {
     vector<const char *> env_args;
     env_to_vec(env_args, args_var);
@@ -572,9 +663,6 @@ int md_config_t::parse_argv(ConfigValues& values,
     else if (ceph_argparse_flag(args, i, "--no-mon-config", (char*)NULL)) {
       values.no_mon_config = true;
     }
-    else if (ceph_argparse_flag(args, i, "--log-early", (char*)NULL)) {
-      values.log_early = true;
-    }
     else if (ceph_argparse_flag(args, i, "--mon-config", (char*)NULL)) {
       values.no_mon_config = false;
     }
@@ -582,6 +670,7 @@ int md_config_t::parse_argv(ConfigValues& values,
       set_val_or_die(values, tracker, "daemonize", "false");
     }
     else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
+      set_val_or_die(values, tracker, "fuse_debug", "true");
       set_val_or_die(values, tracker, "daemonize", "false");
       set_val_or_die(values, tracker, "log_file", "");
       set_val_or_die(values, tracker, "log_to_stderr", "true");
@@ -806,7 +895,7 @@ int md_config_t::injectargs(ConfigValues& values,
 
 void md_config_t::set_val_or_die(ConfigValues& values,
                                 const ConfigTracker& tracker,
-                                const std::string &key,
+                                const std::string_view key,
                                 const std::string &val)
 {
   std::stringstream err;
@@ -819,7 +908,7 @@ void md_config_t::set_val_or_die(ConfigValues& values,
 
 int md_config_t::set_val(ConfigValues& values,
                         const ConfigTracker& tracker,
-                        const std::string &key, const char *val,
+                        const std::string_view key, const char *val,
                         std::stringstream *err_ss)
 {
   if (key.empty()) {
@@ -852,7 +941,7 @@ int md_config_t::set_val(ConfigValues& values,
   return -ENOENT;
 }
 
-int md_config_t::rm_val(ConfigValues& values, const std::string& key)
+int md_config_t::rm_val(ConfigValues& values, const std::string_view key)
 {
   return _rm_val(values, key, CONF_OVERRIDE);
 }
@@ -935,8 +1024,18 @@ void md_config_t::get_config_bl(
   }
 }
 
+std::optional<std::string> md_config_t::get_val_default(std::string_view key)
+{
+  std::string val;
+  const Option *opt = find_option(key);
+  if (opt && (conf_stringify(_get_val_default(*opt), &val) == 0)) {
+    return std::make_optional(std::move(val));
+  }
+  return std::nullopt;
+}
+
 int md_config_t::get_val(const ConfigValues& values,
-                        const std::string &key, char **buf, int len) const
+                        const std::string_view key, char **buf, int len) const
 {
   string k(ConfFile::normalize_key_name(key));
   return _get_val_cstr(values, k, buf, len);
@@ -944,7 +1043,7 @@ int md_config_t::get_val(const ConfigValues& values,
 
 int md_config_t::get_val(
   const ConfigValues& values,
-  const std::string &key,
+  const std::string_view key,
   std::string *val) const
 {
   return conf_stringify(get_val_generic(values, key), val);
@@ -952,29 +1051,28 @@ int md_config_t::get_val(
 
 Option::value_t md_config_t::get_val_generic(
   const ConfigValues& values,
-  const std::string &key) const
+  const std::string_view key) const
 {
-  string k(ConfFile::normalize_key_name(key));
-  return _get_val(values, k);
+  return _get_val(values, key);
 }
 
 Option::value_t md_config_t::_get_val(
   const ConfigValues& values,
-  const std::string &key,
+  const std::string_view key,
   expand_stack_t *stack,
   std::ostream *err) const
 {
   if (key.empty()) {
-    return Option::value_t(boost::blank());
+    return {};
   }
 
   // In key names, leading and trailing whitespace are not significant.
   string k(ConfFile::normalize_key_name(key));
 
-  const Option *o = find_option(key);
+  const Option *o = find_option(k);
   if (!o) {
     // not a valid config option
-    return Option::value_t(boost::blank());
+    return {};
   }
 
   return _get_val(values, *o, stack, err);
@@ -1007,7 +1105,7 @@ Option::value_t md_config_t::_get_val_nometa(const ConfigValues& values,
 
 const Option::value_t& md_config_t::_get_val_default(const Option& o) const
 {
-  bool has_daemon_default = !boost::get<boost::blank>(&o.daemon_value);
+  bool has_daemon_default = (o.daemon_value != Option::value_t{});
   if (is_daemon && has_daemon_default) {
     return o.daemon_value;
   } else {
@@ -1030,17 +1128,19 @@ void md_config_t::early_expand_meta(
 bool md_config_t::finalize_reexpand_meta(ConfigValues& values,
                                         const ConfigTracker& tracker)
 {
-  for (auto& [name, value] : may_reexpand_meta) {
-    set_val(values, tracker, name, value);
-  }
-  
-  if (!may_reexpand_meta.empty()) {
-    // meta expands could have modified anything.  Copy it all out again.
-    update_legacy_vals(values);
-    return true;
-  } else {
-    return false;
+  std::vector<std::string> reexpands;
+  reexpands.swap(may_reexpand_meta);
+  for (auto& name : reexpands) {
+    // always refresh the options if they are in the may_reexpand_meta
+    // map, because the options may have already been expanded with old
+    // meta.
+    const auto &opt_iter = schema.find(name);
+    ceph_assert(opt_iter != schema.end());
+    const Option &opt = opt_iter->second;
+    _refresh(values, opt);
   }
+
+  return !may_reexpand_meta.empty();
 }
 
 Option::value_t md_config_t::_expand_meta(
@@ -1054,7 +1154,7 @@ Option::value_t md_config_t::_expand_meta(
   if (!stack) {
     return in;
   }
-  const std::string *str = boost::get<const std::string>(&in);
+  const auto str = std::get_if<std::string>(&in);
   if (!str) {
     // strings only!
     return in;
@@ -1122,16 +1222,24 @@ Option::value_t md_config_t::_expand_meta(
       } else if (var == "id") {
        out += values.name.get_id();
       } else if (var == "pid") {
-       out += stringify(getpid());
+        char *_pid = getenv("PID");
+        if (_pid) {
+          out += _pid;
+        } else {
+          out += stringify(getpid());
+        }
         if (o) {
-          may_reexpand_meta[o->name] = *str;
+          may_reexpand_meta.push_back(o->name);
         }
       } else if (var == "cctid") {
        out += stringify((unsigned long long)this);
       } else if (var == "home") {
        const char *home = getenv("HOME");
        out = home ? std::string(home) : std::string();
-      } else {
+      } else if (var == "programdata") {
+        const char *home = getenv("ProgramData");
+        out = home ? std::string(home) : std::string();
+      }else {
        if (var == "data_dir") {
          var = data_dir_option;
        }
@@ -1179,7 +1287,7 @@ Option::value_t md_config_t::_expand_meta(
 
 int md_config_t::_get_val_cstr(
   const ConfigValues& values,
-  const std::string &key, char **buf, int len) const
+  const std::stringkey, char **buf, int len) const
 {
   if (key.empty())
     return -EINVAL;
@@ -1198,8 +1306,6 @@ int md_config_t::_get_val_cstr(
     return (l > len) ? -ENAMETOOLONG : 0;
   }
 
-  string k(ConfFile::normalize_key_name(key));
-
   // couldn't find a configuration option with key 'k'
   return -ENOENT;
 }
@@ -1223,28 +1329,20 @@ void md_config_t::get_all_keys(std::vector<std::string> *keys) const {
  * looking. The lowest priority section is the one we look in only if all
  * others had nothing.  This should always be the global section.
  */
-void md_config_t::get_my_sections(const ConfigValues& values,
-                                 std::vector <std::string> &sections) const
+std::vector <std::string>
+md_config_t::get_my_sections(const ConfigValues& values) const
 {
-  _get_my_sections(values, sections);
-}
-
-void md_config_t::_get_my_sections(const ConfigValues& values,
-                                  std::vector <std::string> &sections) const
-{
-  sections.push_back(values.name.to_str());
-
-  sections.push_back(values.name.get_type_name());
-
-  sections.push_back("global");
+  return {values.name.to_str(),
+         values.name.get_type_name().data(),
+         "global"};
 }
 
 // Return a list of all sections
 int md_config_t::get_all_sections(std::vector <std::string> &sections) const
 {
-  for (ConfFile::const_section_iter_t s = cf.sections_begin();
-       s != cf.sections_end(); ++s) {
-    sections.push_back(s->first);
+  for (auto [section_name, section] : cf) {
+    sections.push_back(section_name);
+    std::ignore = section;
   }
   return 0;
 }
@@ -1252,7 +1350,7 @@ int md_config_t::get_all_sections(std::vector <std::string> &sections) const
 int md_config_t::get_val_from_conf_file(
   const ConfigValues& values,
   const std::vector <std::string> &sections,
-  const std::string &key,
+  const std::string_view key,
   std::string &out,
   bool emeta) const
 {
@@ -1270,13 +1368,11 @@ int md_config_t::get_val_from_conf_file(
 
 int md_config_t::_get_val_from_conf_file(
   const std::vector <std::string> &sections,
-  const std::string &key,
+  const std::string_view key,
   std::string &out) const
 {
-  std::vector <std::string>::const_iterator s = sections.begin();
-  std::vector <std::string>::const_iterator s_end = sections.end();
-  for (; s != s_end; ++s) {
-    int ret = cf.read(s->c_str(), key, out);
+  for (auto &s : sections) {
+    int ret = cf.read(s, key, out);
     if (ret == 0) {
       return 0;
     } else if (ret != -ENOENT) {
@@ -1295,6 +1391,7 @@ int md_config_t::_set_val(
   std::string *error_message)
 {
   Option::value_t new_value;
+  ceph_assert(error_message);
   int r = opt.parse_value(raw_val, &new_value, error_message);
   if (r < 0) {
     return r;
@@ -1308,7 +1405,7 @@ int md_config_t::_set_val(
     if (new_value != _get_val_nometa(values, opt)) {
       *error_message = string("Configuration option '") + opt.name +
        "' may not be modified at runtime";
-      return -ENOSYS;
+      return -EPERM;
     }
   }
 
@@ -1347,13 +1444,13 @@ void md_config_t::_refresh(ConfigValues& values, const Option& opt)
 }
 
 int md_config_t::_rm_val(ConfigValues& values,
-                        const std::string& key,
+                        const std::string_view key,
                         int level)
 {
   if (schema.count(key) == 0) {
     return -EINVAL;
   }
-  auto ret = values.rm_val(key, level);
+  auto ret = values.rm_val(std::string{key}, level);
   if (ret < 0) {
     return ret;
   }
@@ -1366,7 +1463,7 @@ int md_config_t::_rm_val(ConfigValues& values,
 
 namespace {
 template<typename Size>
-struct get_size_visitor : public boost::static_visitor<Size>
+struct get_size_visitor
 {
   get_size_visitor() {}
 
@@ -1385,35 +1482,34 @@ struct get_size_visitor : public boost::static_visitor<Size>
 /**
  * Handles assigning from a variant-of-types to a variant-of-pointers-to-types
  */
-template<class Config>
-class assign_visitor : public boost::static_visitor<>
+class assign_visitor
 {
-  Config *conf;
+  ConfigValues *conf;
   Option::value_t val;
   public:
 
-  assign_visitor(Config *conf_, Option::value_t val_)
+  assign_visitor(ConfigValues *conf_, Option::value_t val_)
     : conf(conf_), val(val_)
   {}
 
   template <typename T>
-  void operator()(T Config::* ptr) const
+  void operator()(T ConfigValues::* ptr) const
   {
-    T *member = const_cast<T *>(&(conf->*(boost::get<const T Config::*>(ptr))));
+    T *member = const_cast<T *>(&(conf->*(ptr)));
 
-    *member = boost::get<T>(val);
+    *member = std::get<T>(val);
   }
-  void operator()(uint64_t Config::* ptr) const
+  void operator()(uint64_t ConfigValues::* ptr) const
   {
     using T = uint64_t;
-    auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
-    *member = boost::apply_visitor(get_size_visitor<T>{}, val);
+    auto member = const_cast<T*>(&(conf->*(ptr)));
+    *member = std::visit(get_size_visitor<T>{}, val);
   }
-  void operator()(int64_t Config::* ptr) const
+  void operator()(int64_t ConfigValues::* ptr) const
   {
     using T = int64_t;
-    auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr))));
-    *member = boost::apply_visitor(get_size_visitor<T>{}, val);
+    auto member = const_cast<T*>(&(conf->*(ptr)));
+    *member = std::visit(get_size_visitor<T>{}, val);
   }
 };
 } // anonymous namespace
@@ -1433,18 +1529,18 @@ void md_config_t::update_legacy_val(ConfigValues& values,
                                     md_config_t::member_ptr_t member_ptr)
 {
   Option::value_t v = _get_val(values, opt);
-  boost::apply_visitor(assign_visitor(&values, v), member_ptr);
+  std::visit(assign_visitor(&values, v), member_ptr);
 }
 
 static void dump(Formatter *f, int level, Option::value_t in)
 {
-  if (const bool *v = boost::get<const bool>(&in)) {
+  if (const auto v = std::get_if<bool>(&in)) {
     f->dump_bool(ceph_conf_level_name(level), *v);
-  } else if (const int64_t *v = boost::get<const int64_t>(&in)) {
+  } else if (const auto v = std::get_if<int64_t>(&in)) {
     f->dump_int(ceph_conf_level_name(level), *v);
-  } else if (const uint64_t *v = boost::get<const uint64_t>(&in)) {
+  } else if (const auto v = std::get_if<uint64_t>(&in)) {
     f->dump_unsigned(ceph_conf_level_name(level), *v);
-  } else if (const double *v = boost::get<const double>(&in)) {
+  } else if (const auto v = std::get_if<double>(&in)) {
     f->dump_float(ceph_conf_level_name(level), *v);
   } else {
     f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in);
@@ -1457,14 +1553,16 @@ void md_config_t::diff(
   string name) const
 {
   values.for_each([this, f, &values] (auto& name, auto& configs) {
-    if (configs.size() == 1 &&
-       configs.begin()->first == CONF_DEFAULT) {
-      // we only have a default value; exclude from diff
+    if (configs.empty()) {
       return;
     }
-    f->open_object_section(name.c_str());
+    f->open_object_section(std::string{name}.c_str());
     const Option *o = find_option(name);
-    dump(f, CONF_DEFAULT, _get_val_default(*o));
+    if (configs.size() &&
+       configs.begin()->first != CONF_DEFAULT) {
+      // show compiled-in default only if an override default wasn't provided
+      dump(f, CONF_DEFAULT, _get_val_default(*o));
+    }
     for (auto& j : configs) {
       dump(f, j.first, j.second);
     }
@@ -1473,7 +1571,7 @@ void md_config_t::diff(
   });
 }
 
-void md_config_t::complain_about_parse_errors(CephContext *cct)
+void md_config_t::complain_about_parse_error(CephContext *cct)
 {
-  ::complain_about_parse_errors(cct, &parse_errors);
+  ::complain_about_parse_error(cct, parse_error);
 }