diff options
Diffstat (limited to '')
-rw-r--r-- | src/common/options.cc | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/src/common/options.cc b/src/common/options.cc new file mode 100644 index 000000000..a68e2474a --- /dev/null +++ b/src/common/options.cc @@ -0,0 +1,340 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "acconfig.h" +#include "options.h" +#include "common/Formatter.h" +#include "common/options/build_options.h" + +// Helpers for validators +#include "include/stringify.h" +#include "include/common_fwd.h" +#include <boost/algorithm/string.hpp> +#include <boost/lexical_cast.hpp> +#include <regex> + +// Definitions for enums +#include "common/perf_counters.h" + +// rbd feature and io operation validation +#include "librbd/Features.h" +#include "librbd/io/IoOperations.h" + +using std::ostream; +using std::ostringstream; + +using ceph::Formatter; +using ceph::parse_timespan; + +namespace { +class printer { + ostream& out; +public: + explicit printer(ostream& os) + : out(os) {} + template<typename T> + void operator()(const T& v) const { + out << v; + } + void operator()(std::monostate) const { + return; + } + void operator()(bool v) const { + out << (v ? "true" : "false"); + } + void operator()(double v) const { + out << std::fixed << v << std::defaultfloat; + } + void operator()(const Option::size_t& v) const { + out << v.value; + } + void operator()(const std::chrono::seconds v) const { + out << v.count(); + } + void operator()(const std::chrono::milliseconds v) const { + out << v.count(); + } +}; +} + +ostream& operator<<(ostream& os, const Option::value_t& v) { + printer p{os}; + std::visit(p, v); + return os; +} + +void Option::dump_value(const char *field_name, + const Option::value_t &v, Formatter *f) const +{ + if (v == value_t{}) { + // This should be nil but Formatter doesn't allow it. + f->dump_string(field_name, ""); + return; + } + switch (type) { + case TYPE_INT: + f->dump_int(field_name, std::get<int64_t>(v)); break; + case TYPE_UINT: + f->dump_unsigned(field_name, std::get<uint64_t>(v)); break; + case TYPE_STR: + f->dump_string(field_name, std::get<std::string>(v)); break; + case TYPE_FLOAT: + f->dump_float(field_name, std::get<double>(v)); break; + case TYPE_BOOL: + f->dump_bool(field_name, std::get<bool>(v)); break; + default: + f->dump_stream(field_name) << v; break; + } +} + +int Option::pre_validate(std::string *new_value, std::string *err) const +{ + if (validator) { + return validator(new_value, err); + } else { + return 0; + } +} + +int Option::validate(const Option::value_t &new_value, std::string *err) const +{ + // Generic validation: min + if (min != value_t{}) { + if (new_value < min) { + std::ostringstream oss; + oss << "Value '" << new_value << "' is below minimum " << min; + *err = oss.str(); + return -EINVAL; + } + } + + // Generic validation: max + if (max != value_t{}) { + if (new_value > max) { + std::ostringstream oss; + oss << "Value '" << new_value << "' exceeds maximum " << max; + *err = oss.str(); + return -EINVAL; + } + } + + // Generic validation: enum + if (!enum_allowed.empty() && type == Option::TYPE_STR) { + auto found = std::find(enum_allowed.begin(), enum_allowed.end(), + std::get<std::string>(new_value)); + if (found == enum_allowed.end()) { + std::ostringstream oss; + oss << "'" << new_value << "' is not one of the permitted " + "values: " << joinify(enum_allowed.begin(), + enum_allowed.end(), + std::string(", ")); + *err = oss.str(); + return -EINVAL; + } + } + + return 0; +} + +int Option::parse_value( + const std::string& raw_val, + value_t *out, + std::string *error_message, + std::string *normalized_value) const +{ + std::string val = raw_val; + + int r = pre_validate(&val, error_message); + if (r != 0) { + return r; + } + + if (type == Option::TYPE_INT) { + int64_t f = strict_si_cast<int64_t>(val, error_message); + if (!error_message->empty()) { + return -EINVAL; + } + *out = f; + } else if (type == Option::TYPE_UINT) { + uint64_t f = strict_si_cast<uint64_t>(val, error_message); + if (!error_message->empty()) { + return -EINVAL; + } + *out = f; + } else if (type == Option::TYPE_STR) { + *out = val; + } else if (type == Option::TYPE_FLOAT) { + double f = strict_strtod(val.c_str(), error_message); + if (!error_message->empty()) { + return -EINVAL; + } else { + *out = f; + } + } else if (type == Option::TYPE_BOOL) { + bool b = strict_strtob(val.c_str(), error_message); + if (!error_message->empty()) { + return -EINVAL; + } else { + *out = b; + } + } else if (type == Option::TYPE_ADDR) { + entity_addr_t addr; + if (!addr.parse(val)){ + return -EINVAL; + } + *out = addr; + } else if (type == Option::TYPE_ADDRVEC) { + entity_addrvec_t addr; + if (!addr.parse(val.c_str())){ + return -EINVAL; + } + *out = addr; + } else if (type == Option::TYPE_UUID) { + uuid_d uuid; + if (!uuid.parse(val.c_str())) { + return -EINVAL; + } + *out = uuid; + } else if (type == Option::TYPE_SIZE) { + Option::size_t sz{strict_iecstrtoll(val, error_message)}; + if (!error_message->empty()) { + return -EINVAL; + } + *out = sz; + } else if (type == Option::TYPE_SECS) { + try { + *out = parse_timespan(val); + } catch (const std::invalid_argument& e) { + *error_message = e.what(); + return -EINVAL; + } + } else if (type == Option::TYPE_MILLISECS) { + try { + *out = std::chrono::milliseconds(std::stoull(val)); + } catch (const std::logic_error& e) { + *error_message = e.what(); + return -EINVAL; + } + } else { + ceph_abort(); + } + + r = validate(*out, error_message); + if (r != 0) { + return r; + } + + if (normalized_value) { + *normalized_value = to_str(*out); + } + return 0; +} + +void Option::dump(Formatter *f) const +{ + f->dump_string("name", name); + + f->dump_string("type", type_to_str(type)); + + f->dump_string("level", level_to_str(level)); + + f->dump_string("desc", desc); + f->dump_string("long_desc", long_desc); + + dump_value("default", value, f); + dump_value("daemon_default", daemon_value, f); + + f->open_array_section("tags"); + for (const auto t : tags) { + f->dump_string("tag", t); + } + f->close_section(); + + f->open_array_section("services"); + for (const auto s : services) { + f->dump_string("service", s); + } + f->close_section(); + + f->open_array_section("see_also"); + for (const auto sa : see_also) { + f->dump_string("see_also", sa); + } + f->close_section(); + + if (type == TYPE_STR) { + f->open_array_section("enum_values"); + for (const auto &ea : enum_allowed) { + f->dump_string("enum_value", ea); + } + f->close_section(); + } + + dump_value("min", min, f); + dump_value("max", max, f); + + f->dump_bool("can_update_at_runtime", can_update_at_runtime()); + + f->open_array_section("flags"); + if (has_flag(FLAG_RUNTIME)) { + f->dump_string("option", "runtime"); + } + if (has_flag(FLAG_NO_MON_UPDATE)) { + f->dump_string("option", "no_mon_update"); + } + if (has_flag(FLAG_STARTUP)) { + f->dump_string("option", "startup"); + } + if (has_flag(FLAG_CLUSTER_CREATE)) { + f->dump_string("option", "cluster_create"); + } + if (has_flag(FLAG_CREATE)) { + f->dump_string("option", "create"); + } + f->close_section(); +} + +std::string Option::to_str(const Option::value_t& v) +{ + return stringify(v); +} + +void Option::print(ostream *out) const +{ + *out << name << " - " << desc << "\n"; + *out << " (" << type_to_str(type) << ", " << level_to_str(level) << ")\n"; + if (daemon_value != value_t{}) { + *out << " Default (non-daemon): " << stringify(value) << "\n"; + *out << " Default (daemon): " << stringify(daemon_value) << "\n"; + } else { + *out << " Default: " << stringify(value) << "\n"; + } + if (!enum_allowed.empty()) { + *out << " Possible values: "; + for (auto& i : enum_allowed) { + *out << " " << stringify(i); + } + *out << "\n"; + } + if (min != value_t{}) { + *out << " Minimum: " << stringify(min) << "\n" + << " Maximum: " << stringify(max) << "\n"; + } + *out << " Can update at runtime: " + << (can_update_at_runtime() ? "true" : "false") << "\n"; + if (!services.empty()) { + *out << " Services: " << services << "\n"; + } + if (!tags.empty()) { + *out << " Tags: " << tags << "\n"; + } + if (!see_also.empty()) { + *out << " See also: " << see_also << "\n"; + } + + if (long_desc.size()) { + *out << "\n" << long_desc << "\n"; + } +} + +const std::vector<Option> ceph_options = build_options(); |