summaryrefslogtreecommitdiffstats
path: root/src/common/options.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/options.cc')
-rw-r--r--src/common/options.cc340
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();