From: John Spray Date: Fri, 7 Jul 2017 14:12:18 +0000 (-0400) Subject: common/config: hook in new Option config descriptions X-Git-Tag: v12.1.2~192^2~29 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=08731c3567300b28d83b1ac1c2ba78bb6eae7132;p=ceph.git common/config: hook in new Option config descriptions The C++ class member fields continue to exist for settings defined in common/legacy_config_opts.h, but all the schema information is coming from common/options.cc now. The values in md_config_t::values are automatically copied into the C++ class member fields for legacy config options as needed. Signed-off-by: John Spray --- diff --git a/src/common/config.cc b/src/common/config.cc index 3df1d71fafe..12a9c317e9f 100644 --- a/src/common/config.cc +++ b/src/common/config.cc @@ -15,7 +15,6 @@ #include "common/ceph_argparse.h" #include "common/common_init.h" #include "common/config.h" -#include "common/config_validators.h" #include "include/str_list.h" #include "include/stringify.h" #include "osd/osd_types.h" @@ -73,101 +72,84 @@ int ceph_resolve_file_search(const std::string& filename_list, return ret; } -#define OPTION(name, type, def_val) -#define OPTION_VALIDATOR(name) \ -struct md_config_t::option_##name##_t { \ - typedef decltype(md_config_t::name) type; \ -}; -#define SAFE_OPTION(name, type, def_val) -#define SUBSYS(name, log, gather) -#define DEFAULT_SUBSYS(log, gather) -#include "common/legacy_config_opts.h" -#undef OPTION -#undef OPTION_VALIDATOR -#undef SAFE_OPTION -#undef SUBSYS -#undef DEFAULT_SUBSYS - -namespace { - -template -typename std::enable_if::value, - md_config_t::validator_t>::type create_validator() { - return md_config_t::validator_t(); -} - -template -typename std::enable_if::value, - md_config_t::validator_t>::type create_validator() { - // if T is defined (and not just forward declared), it implies - // that a validator function exists. use a dummy typed pointer to - // pick the correct validator function - return [](std::string *value, std::string *error_message) { - return ::validate(reinterpret_cast(0), value, error_message); - }; -} -} // anonymous namespace md_config_t::md_config_t() : cluster(""), - -#define OPTION_OPT_INT(name, def_val) name(def_val), -#define OPTION_OPT_LONGLONG(name, def_val) name((1LL) * def_val), -#define OPTION_OPT_STR(name, def_val) name(def_val), -#define OPTION_OPT_DOUBLE(name, def_val) name(def_val), -#define OPTION_OPT_FLOAT(name, def_val) name(def_val), -#define OPTION_OPT_BOOL(name, def_val) name(def_val), -#define OPTION_OPT_ADDR(name, def_val) name(def_val), -#define OPTION_OPT_U32(name, def_val) name(def_val), -#define OPTION_OPT_U64(name, def_val) name(((uint64_t)1) * def_val), -#define OPTION_OPT_UUID(name, def_val) name(def_val), -#define OPTION(name, type, def_val) OPTION_##type(name, def_val) -#define OPTION_VALIDATOR(name) -#define SAFE_OPTION(name, type, def_val) OPTION(name, type, def_val) -#define SUBSYS(name, log, gather) -#define DEFAULT_SUBSYS(log, gather) -#include "common/legacy_config_opts.h" -#undef OPTION_OPT_INT -#undef OPTION_OPT_LONGLONG -#undef OPTION_OPT_STR -#undef OPTION_OPT_DOUBLE -#undef OPTION_OPT_FLOAT -#undef OPTION_OPT_BOOL -#undef OPTION_OPT_ADDR -#undef OPTION_OPT_U32 -#undef OPTION_OPT_U64 -#undef OPTION_OPT_UUID -#undef OPTION -#undef OPTION_VALIDATOR -#undef SAFE_OPTION -#undef SUBSYS -#undef DEFAULT_SUBSYS lock("md_config_t", true, false) { - static const std::vector s_config_options = { + init_subsys(); + + // Load the compile-time list of Option into + // a map so that we can resolve keys quickly. + for (const auto &i : ceph_options) { + if (schema.count(i.name)) { + // We may be instantiated pre-logging so send + std::cerr << "Duplicate config key in schema: '" << i.name << "'" + << std::endl; + assert(false); + } + schema.insert({i.name, i}); + } + + // Populate list of legacy_values according to the OPTION() definitions + // Note that this is just setting up our map of name->member ptr. The + // default values etc will get loaded in along with new-style data, + // as all loads write to both the values map, and the legacy + // members if present. + legacy_values = { #define OPTION4(name, type, def_val, safe) \ - config_option{ STRINGIFY(name), type, &md_config_t::name, safe, \ - create_validator() }, + {std::string(STRINGIFY(name)), &md_config_t::name}, #define OPTION(name, type, def_val) OPTION4(name, type, def_val, false) -#define OPTION_VALIDATOR(name) #define SAFE_OPTION(name, type, def_val) OPTION4(name, type, def_val, true) -#define SUBSYS(name, log, gather) -#define DEFAULT_SUBSYS(log, gather) #include "common/legacy_config_opts.h" #undef OPTION4 #undef OPTION -#undef OPTION_VALIDATOR #undef SAFE_OPTION -#undef SUBSYS -#undef DEFAULT_SUBSYS }; - static std::shared_ptr - s_tbl(new std::vector(std::move(s_config_options))); - config_options = s_tbl; - validate_default_settings(); - init_subsys(); + validate_schema(); + + // Load default values from the schema + for (const auto &i : schema) { + // TODO: select daemon default if appropriate. + values[i.first] = i.second.value; + } + + // Copy out values (defaults) into any legacy (C struct member) fields + for (const auto &i : legacy_values) { + const auto &name = i.first; + const auto &option = schema.at(name); + auto ptr = i.second; + + update_legacy_val(option, ptr); + } +} + +/** + * Sanity check schema. Assert out on failures, to ensure any bad changes + * cannot possibly pass any testing and make it into a release. + */ +void md_config_t::validate_schema() +{ + for (const auto &i : schema) { + const auto &opt = i.second; + for (const auto &see_also_key : opt.see_also) { + if (schema.count(see_also_key) == 0) { + std::cerr << "Non-existent see-also key '" << see_also_key + << "' on option '" << opt.name << "'" << std::endl; + assert(false); + } + } + } + + for (const auto &i : legacy_values) { + if (schema.count(i.first) == 0) { + std::cerr << "Schema is missing legacy field '" << i.first << "'" + << std::endl; + assert(false); + } + } } void md_config_t::init_subsys() @@ -176,13 +158,7 @@ void md_config_t::init_subsys() subsys.add(ceph_subsys_##name, STRINGIFY(name), log, gather); #define DEFAULT_SUBSYS(log, gather) \ subsys.add(ceph_subsys_, "none", log, gather); -#define OPTION(a, b, c) -#define OPTION_VALIDATOR(a) -#define SAFE_OPTION(a, b, c) -#include "common/legacy_config_opts.h" -#undef OPTION -#undef OPTION_VALIDATOR -#undef SAFE_OPTION +#include "common/subsys.h" #undef SUBSYS #undef DEFAULT_SUBSYS } @@ -255,7 +231,7 @@ int md_config_t::parse_config_files(const char *conf_files, string &s = *p; if (s.find("$data_dir") != string::npos) { if (data_dir_option.length()) { - list stack; + list stack; expand_meta(s, NULL, stack, warnings); p++; } else { @@ -310,12 +286,13 @@ int md_config_t::parse_config_files_impl(const std::list &conf_file std::vector my_sections; _get_my_sections(my_sections); - for (auto& opt: *config_options) { + 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, false); if (ret == 0) { std::string error_message; - int r = set_val_impl(val, &opt, &error_message); + int r = set_val_impl(val, opt, &error_message); if (warnings != nullptr && (r != 0 || !error_message.empty())) { *warnings << "parse error setting '" << opt.name << "' to '" << val << "'"; @@ -328,7 +305,7 @@ int md_config_t::parse_config_files_impl(const std::list &conf_file } // subsystems? - for (int o = 0; o < subsys.get_num(); o++) { + for (size_t o = 0; o < subsys.get_num(); o++) { std::string as_option("debug_"); as_option += subsys.get_name(o); std::string val; @@ -402,7 +379,7 @@ void md_config_t::_show_config(std::ostream *out, Formatter *f) f->dump_string("name", stringify(name)); f->dump_string("cluster", cluster); } - for (int o = 0; o < subsys.get_num(); o++) { + for (size_t o = 0; o < subsys.get_num(); o++) { if (out) *out << "debug_" << subsys.get_name(o) << " = " << subsys.get_log_level(o) @@ -416,13 +393,14 @@ void md_config_t::_show_config(std::ostream *out, Formatter *f) f->dump_string(debug_name.c_str(), ss.str()); } } - for (auto& opt: *config_options) { + for (const auto& i: schema) { + const Option &opt = i.second; char *buf; _get_val(opt.name, &buf, -1); if (out) *out << opt.name << " = " << buf << std::endl; if (f) - f->dump_string(opt.name, buf); + f->dump_string(opt.name.c_str(), buf); free(buf); } } @@ -528,7 +506,7 @@ int md_config_t::parse_option(std::vector& args, ostream *oss) { int ret = 0; - int o; + size_t o = 0; std::string val; // subsystems? @@ -556,16 +534,16 @@ int md_config_t::parse_option(std::vector& args, return ret; } - const char *option_name = nullptr; + std::string option_name; std::string error_message; o = 0; - for (auto& opt_ref: *config_options) { + for (const auto& opt_iter: schema) { + const Option &opt = opt_iter.second; ostringstream err; - config_option const *opt = &opt_ref; std::string as_option("--"); - as_option += opt->name; - option_name = opt->name; - if (opt->type == OPT_BOOL) { + as_option += opt.name; + option_name = opt.name; + if (opt.type == Option::TYPE_BOOL) { int res; if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(), (char*)NULL)) { @@ -578,7 +556,7 @@ int md_config_t::parse_option(std::vector& args, break; } else { std::string no("--no-"); - no += opt->name; + no += opt.name; if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) { ret = set_val_impl("false", opt, &error_message); break; @@ -591,9 +569,9 @@ int md_config_t::parse_option(std::vector& args, ret = -EINVAL; break; } - if (oss && ((!opt->is_safe()) && - (observers.find(opt->name) == observers.end()))) { - *oss << "You cannot change " << opt->name << " using injectargs.\n"; + if (oss && ((!opt.is_safe()) && + (observers.find(opt.name) == observers.end()))) { + *oss << "You cannot change " << opt.name << " using injectargs.\n"; return -ENOSYS; } ret = set_val_impl(val, opt, &error_message); @@ -603,7 +581,7 @@ int md_config_t::parse_option(std::vector& args, } if (ret != 0 || !error_message.empty()) { - assert(option_name); + assert(!option_name.empty()); if (oss) { *oss << "Parse error setting " << option_name << " to '" << val << "' using injectargs"; @@ -621,7 +599,7 @@ int md_config_t::parse_option(std::vector& args, } } - if (o == (int)config_options->size()) { + if (o == schema.size()) { // ignore ++i; } @@ -666,6 +644,15 @@ void md_config_t::_apply_changes(std::ostream *oss) expand_all_meta(); + // expand_all_meta could have modified anything. Copy it all out again. + for (const auto &i : legacy_values) { + const auto &name = i.first; + const auto &option = schema.at(name); + auto ptr = i.second; + + update_legacy_val(option, ptr); + } + // create the reverse observer mapping, mapping observers to the set of // changed keys that they'll get. rev_obs_map_t robs; @@ -754,59 +741,16 @@ int md_config_t::injectargs(const std::string& s, std::ostream *oss) return ret; } -void md_config_t::set_val_or_die(const char *key, const char *val) +void md_config_t::set_val_or_die(const std::string &key, const char *val) { int ret = set_val(key, val); assert(ret == 0); } -struct is_integer_member : public boost::static_visitor { - template, int>::type = 0> - bool operator()(const T md_config_t::* /* member_ptr */) const { - return true; - } - template::value, int>::type = 0> - bool operator()(const T md_config_t::* /* member_ptr */) const { - return false; - } -}; - -struct is_float_member : public boost::static_visitor { - template, int>::type = 0> - bool operator()(const T md_config_t::* /* member_ptr */) const { - return true; - } - template::value, int>::type = 0> - bool operator()(const T md_config_t::* /* member_ptr */) const { - return false; - } -}; - -bool md_config_t::config_option::is_safe() const { - // for now integer and floating point options considered thread safe - return safe || - boost::apply_visitor(is_integer_member(), md_member_ptr) || - boost::apply_visitor(is_float_member(), md_member_ptr); -} - -md_config_t::config_option const *md_config_t::find_config_option(const std::string &normalized_key) const -{ - auto opt_it = std::find_if(config_options->begin(), - config_options->end(), - [normalized_key](const config_option &opt) -> bool { - return strcmp(normalized_key.c_str(), opt.name) == 0; - }); - return config_options->end() == opt_it ? nullptr : &(*opt_it); -} - -int md_config_t::set_val(const char *key, const char *val, bool meta) +int md_config_t::set_val(const std::string &key, const char *val, bool meta) { Mutex::Locker l(lock); - if (!key) + if (key.empty()) return -EINVAL; if (!val) return -EINVAL; @@ -815,11 +759,13 @@ int md_config_t::set_val(const char *key, const char *val, bool meta) if (meta) expand_meta(v, &std::cerr); + std::cerr << "set_val: expanded '" << std::string(val) << "' to '" << v << "'" << std::endl; + string k(ConfFile::normalize_key_name(key)); // subsystems? if (strncmp(k.c_str(), "debug_", 6) == 0) { - for (int o = 0; o < subsys.get_num(); o++) { + for (size_t o = 0; o < subsys.get_num(); o++) { std::string as_option = "debug_" + subsys.get_name(o); if (k == as_option) { int log, gather; @@ -837,11 +783,12 @@ int md_config_t::set_val(const char *key, const char *val, bool meta) } } - config_option const *opt = find_config_option(k); - if (opt) { - if ((!opt->is_safe()) && internal_safe_to_start_threads) { + const auto &opt_iter = schema.find(k); + if (opt_iter != schema.end()) { + const Option &opt = opt_iter->second; + if ((!opt.is_safe()) && internal_safe_to_start_threads) { // If threads have been started and the option is not thread safe - if (observers.find(opt->name) == observers.end()) { + if (observers.find(opt.name) == observers.end()) { // And there is no observer to safely change it... // You lose. return -ENOSYS; @@ -849,8 +796,7 @@ int md_config_t::set_val(const char *key, const char *val, bool meta) } std::string error_message; - int r = set_val_impl(v, opt, &error_message); - return r; + return set_val_impl(v, opt, &error_message); } // couldn't find a configuration option with key 'key' @@ -858,56 +804,48 @@ int md_config_t::set_val(const char *key, const char *val, bool meta) } -int md_config_t::get_val(const char *key, char **buf, int len) const +int md_config_t::get_val(const std::string &key, char **buf, int len) const { Mutex::Locker l(lock); return _get_val(key, buf,len); } -md_config_t::config_value_t md_config_t::get_val_generic(const char *key) const +Option::value_t md_config_t::get_val_generic(const std::string &key) const { Mutex::Locker l(lock); return _get_val(key); } -class get_value_generic_visitor : public boost::static_visitor { - md_config_t const *conf; -public: - explicit get_value_generic_visitor(md_config_t const *conf_) : conf(conf_) { } - template md_config_t::config_value_t operator()(const T md_config_t::* member_ptr) { - return md_config_t::config_value_t(conf->*member_ptr); - } -}; - -md_config_t::config_value_t md_config_t::_get_val(const char *key) const +Option::value_t md_config_t::_get_val(const std::string &key) const { assert(lock.is_locked()); - if (!key) - return config_value_t(invalid_config_value_t()); + if (key.empty()) { + return Option::value_t(boost::blank()); + } // In key names, leading and trailing whitespace are not significant. string k(ConfFile::normalize_key_name(key)); - config_option const *opt = find_config_option(k); - if (!opt) { - return config_value_t(invalid_config_value_t()); + const auto &opt_iter = schema.find(k); + if (opt_iter != schema.end()) { + // Using .at() is safe because all keys in the schema always have + // entries in ::values + return values.at(k); + } else { + return Option::value_t(boost::blank()); } - get_value_generic_visitor gvv(this); - return boost::apply_visitor(gvv, opt->md_member_ptr); } -int md_config_t::_get_val(const char *key, std::string *value) const { +int md_config_t::_get_val(const std::string &key, std::string *value) const { assert(lock.is_locked()); std::string normalized_key(ConfFile::normalize_key_name(key)); - config_value_t config_value = _get_val(normalized_key.c_str()); - if (!boost::get(&config_value)) { + Option::value_t config_value = _get_val(normalized_key.c_str()); + if (!boost::get(&config_value)) { ostringstream oss; if (bool *flag = boost::get(&config_value)) { oss << (*flag ? "true" : "false"); - } else if (float *fp = boost::get(&config_value)) { - oss << std::fixed << *fp ; } else if (double *dp = boost::get(&config_value)) { oss << std::fixed << *dp ; } else { @@ -919,15 +857,15 @@ int md_config_t::_get_val(const char *key, std::string *value) const { return -ENOENT; } -int md_config_t::_get_val(const char *key, char **buf, int len) const +int md_config_t::_get_val(const std::string &key, char **buf, int len) const { assert(lock.is_locked()); - if (!key) + if (key.empty()) return -EINVAL; string val ; - if (!_get_val(key, &val)) { + if (_get_val(key, &val) == 0) { int l = val.length() + 1; if (len == -1) { *buf = (char*)malloc(l); @@ -942,7 +880,7 @@ int md_config_t::_get_val(const char *key, char **buf, int len) const string k(ConfFile::normalize_key_name(key)); // subsys? - for (int o = 0; o < subsys.get_num(); o++) { + for (size_t o = 0; o < subsys.get_num(); o++) { std::string as_option = "debug_" + subsys.get_name(o); if (k == as_option) { if (len == -1) { @@ -962,14 +900,15 @@ void md_config_t::get_all_keys(std::vector *keys) const { const std::string negative_flag_prefix("no_"); keys->clear(); - keys->reserve(config_options->size()); - for (auto& opt: *config_options) { + keys->reserve(schema.size()); + for (const auto &i: schema) { + const Option &opt = i.second; keys->push_back(opt.name); - if (opt.type == OPT_BOOL) { + if (opt.type == Option::TYPE_BOOL) { keys->push_back(negative_flag_prefix + opt.name); } } - for (int i = 0; i < subsys.get_num(); ++i) { + for (size_t i = 0; i < subsys.get_num(); ++i) { keys->push_back("debug_" + subsys.get_name(i)); } } @@ -1007,14 +946,14 @@ int md_config_t::get_all_sections(std::vector §ions) const } int md_config_t::get_val_from_conf_file(const std::vector §ions, - const char *key, std::string &out, bool emeta) const + const std::string &key, std::string &out, bool emeta) const { Mutex::Locker l(lock); return _get_val_from_conf_file(sections, key, out, emeta); } int md_config_t::_get_val_from_conf_file(const std::vector §ions, - const char *key, std::string &out, bool emeta) const + const std::string &key, std::string &out, bool emeta) const { assert(lock.is_locked()); std::vector ::const_iterator s = sections.begin(); @@ -1032,117 +971,115 @@ int md_config_t::_get_val_from_conf_file(const std::vector §io return -ENOENT; } -int md_config_t::set_val_impl(const std::string &val, config_option const *opt, +int md_config_t::set_val_impl(const std::string &val, const Option &opt, std::string *error_message) { assert(lock.is_locked()); - std::string value(val); - if (opt->validator) { - int r = opt->validator(&value, error_message); + + // TODO: hook validators back in +#if 0 + if (opt.validator) { + int r = opt.validator(&value, error_message); if (r < 0) { return r; } } +#endif - int ret = set_val_raw(value.c_str(), opt); - if (ret) - return ret; - changed.insert(opt->name); - return 0; -} - -template struct strtox_helper; - -template<> struct strtox_helper { - static inline void apply(const char *val, float &x, std::string &err) { - x = strict_strtof(val, &err); - } -}; - -template<> struct strtox_helper { - static inline void apply(const char *val, double &x, std::string &err) { - x = strict_strtod(val, &err); - } -}; - -template static inline int strict_strtox(const char *val, T &x) { - std::string err; - strtox_helper::apply(val, x, err); - return err.empty() ? 0 : -EINVAL; -} - -class set_value_visitor : public boost::static_visitor { - md_config_t const *conf; - const char *val; -public: - explicit set_value_visitor(md_config_t const *conf_, const char *val_) : - conf(conf_), val(val_) { } - - int operator()(const std::string md_config_t::* member_ptr) { - auto *ptr = const_cast(&(conf->*member_ptr)); - *ptr = val ? val : ""; - return 0; - } - - int operator()(const bool md_config_t::* member_ptr) { - bool *ptr = const_cast(&(conf->*member_ptr)); - if (strcasecmp(val, "false") == 0) { - *ptr = false; - } else if (strcasecmp(val, "true") == 0) { - *ptr = true; + Option::value_t new_value; + if (opt.type == Option::TYPE_INT) { + std::string err; + int64_t f = strict_si_cast(val.c_str(), &err); + if (!err.empty()) { + return -EINVAL; + } + new_value = f; + } else if (opt.type == Option::TYPE_STR) { + new_value = val; + } else if (opt.type == Option::TYPE_FLOAT) { + std::string err; + double f = strict_strtod(val.c_str(), &err); + if (!err.empty()) { + return -EINVAL; + } else { + new_value = f; + } + } else if (opt.type == Option::TYPE_BOOL) { + if (strcasecmp(val.c_str(), "false") == 0) { + new_value = false; + } else if (strcasecmp(val.c_str(), "true") == 0) { + new_value = true; } else { std::string err; - int b = strict_strtol(val, 10, &err); + int b = strict_strtol(val.c_str(), 10, &err); if (!err.empty()) { return -EINVAL; } - *ptr = !!b; + new_value = !!b; } - return 0; + } else if (opt.type == Option::TYPE_ADDR) { + entity_addr_t addr; + if (!addr.parse(val.c_str())){ + return -EINVAL; + } + new_value = addr; + } else if (opt.type == Option::TYPE_UUID) { + uuid_d uuid; + if (!uuid.parse(val.c_str())) { + return -EINVAL; + } + new_value = uuid; + } else { + ceph_abort(); } - - // type has parse() member function - template, int>::type = 0> - int operator()(const T md_config_t::* member_ptr) { - T *obj = const_cast(&(conf->*member_ptr)); - if (!obj->parse(val)) { - return -EINVAL; - } - return 0; - } - // float, double - template, int>::type = 0> - int operator()(const T md_config_t::* member_ptr) { - T* ptr = const_cast(&(conf->*member_ptr)); - return strict_strtox(val, *ptr); - } + // Apply the value to its entry in the `values` map + values[opt.name] = new_value; - // integers - template::value && - !boost::is_same::value, int>::type = 0> - int operator()(const T md_config_t::* member_ptr) { - std::string err; - T f = strict_si_cast(val, &err); - if (!err.empty()) { - return -EINVAL; - } - T *ptr = const_cast(&(conf->*member_ptr)); - *ptr = f; - return 0; + // Apply the value to its legacy field, if it has one + auto legacy_ptr_iter = legacy_values.find(std::string(opt.name)); + if (legacy_ptr_iter != legacy_values.end()) { + update_legacy_val(opt, legacy_ptr_iter->second); + } + + changed.insert(opt.name); + return 0; +} + +/** + * Handles assigning from a variant-of-types to a variant-of-pointers-to-types + */ +class assign_visitor : public boost::static_visitor<> +{ + md_config_t *conf; + Option::value_t val; + public: + + assign_visitor(md_config_t *conf_, Option::value_t val_) + : conf(conf_), val(val_) + {} + + template + void operator()( T md_config_t::* ptr) const + { + T *member = const_cast(&(conf->*(boost::get(ptr)))); + *member = boost::get(val); } }; -int md_config_t::set_val_raw(const char *val, config_option const *opt) +void md_config_t::update_legacy_val(const Option &opt, + md_config_t::member_ptr_t member_ptr) { - assert(lock.is_locked()); - set_value_visitor svv(this, val); - return boost::apply_visitor(svv, opt->md_member_ptr); + if (boost::get(&values.at(opt.name))) { + // This shouldn't happen, but if it does then just don't even + // try to assign to the legacy field. + return; + } + + boost::apply_visitor(assign_visitor(this, values.at(opt.name)), member_ptr); } + static const char *CONF_METAVARIABLES[] = { "data_dir", // put this first: it may contain some of the others "cluster", "type", "name", "host", "num", "id", "pid", "cctid" @@ -1154,27 +1091,29 @@ void md_config_t::expand_all_meta() { // Expand all metavariables ostringstream oss; - for (auto& opt: *config_options) { - std::string *str; - opt.conf_ptr(str, this); - if (str) { - list stack; + for (const auto &i : schema) { + const Option &opt = i.second; + + if (opt.type == Option::TYPE_STR) { + list stack; + std::string *str = boost::get(&(values.at(opt.name))); + assert(str != nullptr); // Non-string values should never get in expand_meta(*str, &opt, stack, &oss); } } cerr << oss.str(); } -bool md_config_t::expand_meta(std::string &origval, +bool md_config_t::expand_meta(std::string &val, std::ostream *oss) const { - list stack; - return expand_meta(origval, NULL, stack, oss); + list stack; + return expand_meta(val, NULL, stack, oss); } bool md_config_t::expand_meta(std::string &origval, - config_option const *opt, - std::list stack, + const Option *opt, + std::list stack, std::ostream *oss) const { assert(lock.is_locked()); @@ -1186,25 +1125,22 @@ bool md_config_t::expand_meta(std::string &origval, // ignore an expansion loop and create a human readable // message about it if (opt) { - for (list::iterator i = stack.begin(); - i != stack.end(); - ++i) { - if (strcmp(opt->name, (*i)->name) == 0) { + for (const auto stack_ptr : stack) { + if (opt->name == stack_ptr->name) { *oss << "variable expansion loop at " << opt->name << "=" << origval << std::endl; *oss << "expansion stack: " << std::endl; - for (list::iterator j = stack.begin(); - j != stack.end(); - ++j) { - *oss << (*j)->name << "=" << *((*j)->conf_ptr(this)) << std::endl; + for (const auto j : stack) { + std::string val; + _get_val(j->name, &val); + *oss << j->name << "=" << val << std::endl; } return false; } } - } - if (opt) stack.push_front(opt); + } bool found_meta = false; string out; @@ -1285,22 +1221,25 @@ bool md_config_t::expand_meta(std::string &origval, if (!expanded) { // config option? - for (auto& opt: *config_options) { - if (var == opt.name) { - string *origval; - opt.conf_ptr(origval, const_cast(this)); - if (origval) { - expand_meta(*origval, &opt, stack, oss); - out += *origval; - } else { - char *vv = NULL; - _get_val(opt.name, &vv, -1); - out += vv; - free(vv); - } - expanded = true; - break; - } + const auto other_opt_iter = schema.find(var); + if (other_opt_iter != schema.end()) { + const Option &other_opt = other_opt_iter->second; + if (other_opt.type == Option::TYPE_STR) { + // The referenced option is a string, it may need substitution + // before inserting. + Option::value_t *other_val_ptr = const_cast(&(values.at(other_opt.name))); + std::string *other_opt_val = boost::get(other_val_ptr); + expand_meta(*other_opt_val, &other_opt, stack, oss); + out += *other_opt_val; + } else { + // The referenced option is not a string: retrieve and insert + // its stringized form. + char *vv = NULL; + _get_val(other_opt.name, &vv, -1); + out += vv; + free(vv); + } + expanded = true; } } } @@ -1341,7 +1280,8 @@ void md_config_t::diff_helper( char local_buf[4096]; char other_buf[4096]; - for (auto& opt : *config_options) { + for (const auto &i : schema) { + const Option &opt = i.second; if (!setting.empty()) { if (setting != opt.name) { continue; @@ -1378,18 +1318,3 @@ void md_config_t::complain_about_parse_errors(CephContext *cct) ::complain_about_parse_errors(cct, &parse_errors); } -void md_config_t::validate_default_settings() { - Mutex::Locker l(lock); - for (auto &opt : *config_options) { - // normalize config defaults using their validator - if (opt.validator) { - std::string value; - int r = _get_val(opt.name, &value); - assert(r == 0); - - std::string error_message; - r = set_val_impl(value.c_str(), &opt, &error_message); - assert(r == 0); - } - } -} diff --git a/src/common/config.h b/src/common/config.h index 7aa9917982a..c63bc1a6527 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -17,9 +17,11 @@ #include "common/ConfUtils.h" #include "common/entity_name.h" +#include "common/code_environment.h" #include "common/Mutex.h" #include "log/SubsystemMap.h" #include "common/config_obs.h" +#include "common/options.h" #define OSD_REP_PRIMARY 0 #define OSD_REP_SPLAY 1 @@ -64,6 +66,13 @@ extern const char *CEPH_CONF_FILE_DEFAULT; */ struct md_config_t { public: + typedef boost::variant member_ptr_t; + /* Maps configuration options to the observer listening for them. */ typedef std::multimap obs_map_t; @@ -71,81 +80,27 @@ public: * apply_changes */ typedef std::set < std::string > changed_set_t; - struct invalid_config_value_t { }; - typedef boost::variant config_value_t; - typedef boost::variant member_ptr_t; + /* + * Mapping from legacy config option names to class members + */ + std::map legacy_values; + + /** + * The configuration schema, in the form of Option objects describing + * possible settings. + */ + std::map schema; + + /** + * The current values of all settings described by the schema + */ + std::map values; typedef enum { OPT_INT, OPT_LONGLONG, OPT_STR, OPT_DOUBLE, OPT_FLOAT, OPT_BOOL, OPT_ADDR, OPT_U32, OPT_U64, OPT_UUID } opt_type_t; - typedef std::function validator_t; - - class config_option { - public: - const char *name; - opt_type_t type; - md_config_t::member_ptr_t md_member_ptr; - bool safe; // promise to access it only via md_config_t::get_val - validator_t validator; - private: - template struct get_typed_pointer_visitor : public boost::static_visitor { - md_config_t const *conf; - explicit get_typed_pointer_visitor(md_config_t const *conf_) : conf(conf_) { } - template, int>::type = 0> - T const *operator()(const U md_config_t::* member_ptr) { - return &(conf->*member_ptr); - } - template::value, int>::type = 0> - T const *operator()(const U md_config_t::* member_ptr) { - return nullptr; - } - }; - public: - // is it OK to alter the value when threads are running? - bool is_safe() const; - // Given a configuration, return a pointer to this option inside - // that configuration. - template void conf_ptr(T const *&ptr, md_config_t const *conf) const { - get_typed_pointer_visitor gtpv(conf); - ptr = boost::apply_visitor(gtpv, md_member_ptr); - } - template void conf_ptr(T *&ptr, md_config_t *conf) const { - get_typed_pointer_visitor gtpv(conf); - ptr = const_cast(boost::apply_visitor(gtpv, md_member_ptr)); - } - template T const *conf_ptr(md_config_t const *conf) const { - get_typed_pointer_visitor gtpv(conf); - return boost::apply_visitor(gtpv, md_member_ptr); - } - template T *conf_ptr(md_config_t *conf) const { - get_typed_pointer_visitor gtpv(conf); - return const_cast(boost::apply_visitor(gtpv, md_member_ptr)); - } - }; - // Create a new md_config_t structure. md_config_t(); ~md_config_t(); @@ -188,21 +143,21 @@ public: // Set a configuration value, or crash // Metavariables will be expanded. - void set_val_or_die(const char *key, const char *val); + void set_val_or_die(const std::string &key, const char *val); // Set a configuration value. // Metavariables will be expanded. - int set_val(const char *key, const char *val, bool meta=true); - int set_val(const char *key, const string& s, bool meta=true) { + int set_val(const std::string &key, const char *val, bool meta=true); + int set_val(const std::string &key, const string& s, bool meta=true) { return set_val(key, s.c_str(), meta); } // Get a configuration value. // No metavariables will be returned (they will have already been expanded) - int get_val(const char *key, char **buf, int len) const; - int _get_val(const char *key, char **buf, int len) const; - config_value_t get_val_generic(const char *key) const; - template T get_val(const char *key) const; + int get_val(const std::string &key, char **buf, int len) const; + int _get_val(const std::string &key, char **buf, int len) const; + Option::value_t get_val_generic(const std::string &key) const; + template T get_val(const std::string &key) const; void get_all_keys(std::vector *keys) const; @@ -215,7 +170,7 @@ public: // Get a value from the configuration file that we read earlier. // Metavariables will be expanded if emeta is true. int get_val_from_conf_file(const std::vector §ions, - const char *key, std::string &out, bool emeta) const; + std::string const &key, std::string &out, bool emeta) const; /// dump all config values to a stream void show_config(std::ostream& out); @@ -236,16 +191,17 @@ public: void complain_about_parse_errors(CephContext *cct); private: + void validate_schema(); void validate_default_settings(); - int _get_val(const char *key, std::string *value) const; - config_value_t _get_val(const char *key) const; + int _get_val(const std::string &key, std::string *value) const; + Option::value_t _get_val(const std::string &key) const; void _show_config(std::ostream *out, Formatter *f); void _get_my_sections(std::vector §ions) const; int _get_val_from_conf_file(const std::vector §ions, - const char *key, std::string &out, bool emeta) const; + const std::string &key, std::string &out, bool emeta) const; int parse_option(std::vector& args, std::vector::iterator& i, @@ -255,9 +211,15 @@ private: int parse_config_files_impl(const std::list &conf_files, std::ostream *warnings); - int set_val_impl(const std::string &val, config_option const *opt, + int set_val_impl(const std::string &val, const Option &opt, std::string *error_message); - int set_val_raw(const char *val, config_option const *opt); + + template + void assign_member(member_ptr_t ptr, const Option::value_t &val); + + + void update_legacy_val(const Option &opt, + md_config_t::member_ptr_t member); void init_subsys(); @@ -276,8 +238,8 @@ public: // for global_init } private: bool expand_meta(std::string &val, - config_option const *opt, - std::list stack, + const Option *opt, + std::list stack, std::ostream *oss) const; /// expand all metavariables in config structure. @@ -306,28 +268,22 @@ public: // These C members are consumed by code that was written before // the new options.cc infrastructure: all newer code should // be consume options via explicit get() rather than C members. -#define OPTION_OPT_INT(name) const int name; -#define OPTION_OPT_LONGLONG(name) const long long name; -#define OPTION_OPT_STR(name) const std::string name; -#define OPTION_OPT_DOUBLE(name) const double name; -#define OPTION_OPT_FLOAT(name) const float name; -#define OPTION_OPT_BOOL(name) const bool name; -#define OPTION_OPT_ADDR(name) const entity_addr_t name; -#define OPTION_OPT_U32(name) const uint32_t name; -#define OPTION_OPT_U64(name) const uint64_t name; -#define OPTION_OPT_UUID(name) const uuid_d name; +#define OPTION_OPT_INT(name) int64_t name; +#define OPTION_OPT_LONGLONG(name) int64_t name; +#define OPTION_OPT_STR(name) std::string name; +#define OPTION_OPT_DOUBLE(name) double name; +#define OPTION_OPT_FLOAT(name) double name; +#define OPTION_OPT_BOOL(name) bool name; +#define OPTION_OPT_ADDR(name) entity_addr_t name; +#define OPTION_OPT_U32(name) int64_t name; +#define OPTION_OPT_U64(name) int64_t name; +#define OPTION_OPT_UUID(name) uuid_d name; #define OPTION(name, ty, init) \ public: \ - OPTION_##ty(name) \ - struct option_##name##_t; -#define OPTION_VALIDATOR(name) + OPTION_##ty(name) #define SAFE_OPTION(name, ty, init) \ protected: \ - OPTION_##ty(name) \ - public: \ - struct option_##name##_t; -#define SUBSYS(name, log, gather) -#define DEFAULT_SUBSYS(log, gather) + OPTION_##ty(name) #include "common/legacy_config_opts.h" #undef OPTION_OPT_INT #undef OPTION_OPT_LONGLONG @@ -340,11 +296,9 @@ public: #undef OPTION_OPT_U64 #undef OPTION_OPT_UUID #undef OPTION -#undef OPTION_VALIDATOR #undef SAFE_OPTION -#undef SUBSYS -#undef DEFAULT_SUBSYS +public: unsigned get_osd_pool_default_min_size() const { return osd_pool_default_min_size ? MIN(osd_pool_default_min_size, osd_pool_default_size) : @@ -358,11 +312,6 @@ public: mutable Mutex lock; friend class test_md_config_t; -protected: - // Tests and possibly users expect options to appear in the output - // of ceph-conf in the same order as declared in config_opts.h - std::shared_ptr> config_options; - config_option const *find_config_option(const std::string& normalized_key) const; }; template @@ -379,35 +328,26 @@ struct get_typed_value_visitor : public boost::static_visitor { } }; -template T md_config_t::get_val(const char *key) const { - config_value_t generic_val = this->get_val_generic(key); +template T md_config_t::get_val(const std::string &key) const { + Option::value_t generic_val = this->get_val_generic(key); get_typed_value_visitor gtv; return boost::apply_visitor(gtv, generic_val); } -inline std::ostream& operator<<(std::ostream& o, const md_config_t::invalid_config_value_t& ) { +inline std::ostream& operator<<(std::ostream& o, const boost::blank& ) { return o << "INVALID_CONFIG_VALUE"; } int ceph_resolve_file_search(const std::string& filename_list, std::string& result); -typedef md_config_t::config_option config_option; - - enum config_subsys_id { ceph_subsys_, // default -#define OPTION(a,b,c) -#define OPTION_VALIDATOR(name) -#define SAFE_OPTION(a,b,c) #define SUBSYS(name, log, gather) \ ceph_subsys_##name, #define DEFAULT_SUBSYS(log, gather) -#include "common/legacy_config_opts.h" +#include "common/subsys.h" #undef SUBSYS -#undef OPTION -#undef OPTION_VALIDATOR -#undef SAFE_OPTION #undef DEFAULT_SUBSYS ceph_subsys_max }; diff --git a/src/common/options.cc b/src/common/options.cc index 6618779a741..316fca1b57f 100644 --- a/src/common/options.cc +++ b/src/common/options.cc @@ -22,11 +22,11 @@ const std::vector