From c3dd17fd87e11f296e4926ed754d2f9c4e326b10 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Tue, 13 Feb 2018 16:36:35 +0800 Subject: [PATCH] common/config: specialize the settings for time duration because of the ADL, boost::detail::variant::printer is not able to find operator<<(ostream&, chrono::seconds) even the latter is defined. so we need to define our own printer. and print_value() function, so it can be used out of the compilation unit. Signed-off-by: Kefu Chai --- src/common/config.cc | 21 ++--- src/common/options.cc | 135 +++++++++++++++++++++++++++++---- src/common/options.h | 10 ++- src/mon/ConfigMonitor.cc | 4 +- src/test/common/test_config.cc | 14 ++++ 5 files changed, 156 insertions(+), 28 deletions(-) diff --git a/src/common/config.cc b/src/common/config.cc index b0912f90ae9..51ee54a80c0 100644 --- a/src/common/config.cc +++ b/src/common/config.cc @@ -290,14 +290,14 @@ int md_config_t::set_mon_vals(CephContext *cct, const map& kv) ceph_abort(); } } - for (auto& i : values) { - auto j = i.second.find(CONF_MON); - if (j != i.second.end()) { - if (kv.find(i.first) == kv.end()) { - ldout(cct,10) << __func__ << " " << i.first - << " cleared (was " << j->second << ")" + for (const auto& [name,configs] : values) { + auto j = configs.find(CONF_MON); + if (j != configs.end()) { + if (kv.find(name) == kv.end()) { + ldout(cct,10) << __func__ << " " << name + << " cleared (was " << Option::to_str(j->second) << ")" << dendl; - _rm_val(i.first, CONF_MON); + _rm_val(name, CONF_MON); } } } @@ -1174,10 +1174,11 @@ Option::value_t md_config_t::_expand_meta( // substitution loop; break the cycle if (err) { *err << "variable expansion loop at " << var << "=" - << *match->second << "\n" + << Option::to_str(*match->second) << "\n" << "expansion stack:\n"; for (auto i = stack->rbegin(); i != stack->rend(); ++i) { - *err << i->first->name << "=" << *i->second << "\n"; + *err << i->first->name << "=" + << Option::to_str(*i->second) << "\n"; } } return Option::value_t(std::string("$") + o->name); @@ -1492,7 +1493,7 @@ static void dump(Formatter *f, int level, Option::value_t in) } else if (const double *v = boost::get(&in)) { f->dump_float(ceph_conf_level_name(level), *v); } else { - f->dump_stream(ceph_conf_level_name(level)) << in; + f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in); } } diff --git a/src/common/options.cc b/src/common/options.cc index 2d6b957b52b..1588d364d37 100644 --- a/src/common/options.cc +++ b/src/common/options.cc @@ -17,6 +17,39 @@ // rbd feature validation #include "librbd/Features.h" +namespace { +class printer : public boost::static_visitor<> { + ostream& out; +public: + explicit printer(ostream& os) + : out(os) {} + template + void operator()(const T& v) const { + out << v; + } + void operator()(boost::blank blank) 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(); + } +}; +} + +ostream& operator<<(ostream& os, const Option::value_t& v) { + printer p{os}; + v.apply_visitor(p); + return os; +} void Option::dump_value(const char *field_name, const Option::value_t &v, Formatter *f) const @@ -24,21 +57,21 @@ void Option::dump_value(const char *field_name, if (boost::get(&v)) { // This should be nil but Formatter doesn't allow it. f->dump_string(field_name, ""); - } else if (type == TYPE_UINT) { - f->dump_unsigned(field_name, boost::get(v)); - } else if (type == TYPE_INT) { - f->dump_int(field_name, boost::get(v)); - } else if (type == TYPE_STR) { - f->dump_string(field_name, boost::get(v)); - } else if (type == TYPE_FLOAT) { - f->dump_float(field_name, boost::get(v)); - } else if (type == TYPE_BOOL) { - f->dump_bool(field_name, boost::get(v)); - } else if (type == TYPE_SIZE) { - auto bytes = boost::get(v); - f->dump_stream(field_name) << prettybyte_t(bytes.value); - } else { - f->dump_stream(field_name) << v; + return; + } + switch (type) { + case TYPE_INT: + f->dump_int(field_name, boost::get(v)); break; + case TYPE_UINT: + f->dump_unsigned(field_name, boost::get(v)); break; + case TYPE_STR: + f->dump_string(field_name, boost::get(v)); break; + case TYPE_FLOAT: + f->dump_float(field_name, boost::get(v)); break; + case TYPE_BOOL: + f->dump_bool(field_name, boost::get(v)); break; + default: + f->dump_stream(field_name) << v; break; } } @@ -91,6 +124,72 @@ int Option::validate(const Option::value_t &new_value, std::string *err) const return 0; } +namespace { +template +std::chrono::seconds +do_parse_duration(const char* unit, string val, + size_t start, size_t* new_start) +{ + auto found = val.find(unit, start); + if (found == val.npos) { + *new_start = start; + return Duration{0}; + } + val[found] = '\0'; + string err; + char* s = &val[start]; + auto intervals = strict_strtoll(s, 10, &err); + if (!err.empty()) { + throw invalid_argument(s); + } + auto secs = chrono::duration_cast(Duration{intervals}); + *new_start = found + strlen(unit); + return secs; +} + +std::chrono::seconds parse_duration(const std::string& s) +{ + using namespace std::chrono; + auto secs = 0s; + size_t start = 0; + size_t new_start = 0; + using days_t = duration>; + auto v = s; + v.erase(std::remove_if(begin(v), end(v), + [](char c){ return std::isspace(c);}), end(v)); + if (auto delta = do_parse_duration("days", v, start, &new_start); + delta.count()) { + start = new_start; + secs += delta; + } + if (auto delta = do_parse_duration("hours", v, start, &new_start); + delta.count()) { + start = new_start; + secs += delta; + } + if (auto delta = do_parse_duration("minutes", v, start, &new_start); + delta.count()) { + start = new_start; + secs += delta; + } + if (auto delta = do_parse_duration("seconds", v, start, &new_start); + delta.count()) { + start = new_start; + secs += delta; + } + if (new_start == 0) { + string err; + if (auto delta = std::chrono::seconds{strict_strtoll(s.c_str(), 10, &err)}; + err.empty()) { + secs += delta; + } else { + throw invalid_argument(err); + } + } + return secs; +} +} // anonymous namespace + int Option::parse_value( const std::string& raw_val, value_t *out, @@ -155,6 +254,12 @@ int Option::parse_value( return -EINVAL; } *out = sz; + } else if (type == Option::TYPE_SECS) { + try { + *out = parse_duration(val); + } catch (const invalid_argument&) { + return -EINVAL; + } } else { ceph_abort(); } diff --git a/src/common/options.h b/src/common/options.h index 8504ba891d1..7643ea8397b 100644 --- a/src/common/options.h +++ b/src/common/options.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -20,6 +21,7 @@ struct Option { TYPE_ADDR, TYPE_UUID, TYPE_SIZE, + TYPE_SECS, }; const char *type_to_str(type_t t) const { @@ -32,6 +34,7 @@ struct Option { case TYPE_ADDR: return "entity_addr_t"; case TYPE_UUID: return "uuid_d"; case TYPE_SIZE: return "size_t"; + case TYPE_SECS: return "secs"; default: return "unknown"; } } @@ -83,6 +86,7 @@ struct Option { double, bool, entity_addr_t, + std::chrono::seconds, size_t, uuid_d>; const std::string name; @@ -154,6 +158,8 @@ struct Option { value = uuid_d(); break; case TYPE_SIZE: value = size_t{0}; break; + case TYPE_SECS: + value = std::chrono::seconds{0}; break; default: ceph_abort(); } @@ -202,6 +208,8 @@ struct Option { v = bool(new_value); break; case TYPE_SIZE: v = size_t{static_cast(new_value)}; break; + case TYPE_SECS: + v = std::chrono::seconds{new_value}; break; default: std::cerr << "Bad type in set_value: " << name << ": " << typeid(T).name() << std::endl; @@ -316,7 +324,7 @@ struct Option { (has_flag(FLAG_RUNTIME) || type == TYPE_BOOL || type == TYPE_INT || type == TYPE_UINT || type == TYPE_FLOAT - || type == TYPE_SIZE) + || type == TYPE_SIZE || type == TYPE_SECS) && !has_flag(FLAG_STARTUP) && !has_flag(FLAG_CLUSTER_CREATE) && !has_flag(FLAG_CREATE); diff --git a/src/mon/ConfigMonitor.cc b/src/mon/ConfigMonitor.cc index 6614919662f..ef02643b073 100644 --- a/src/mon/ConfigMonitor.cc +++ b/src/mon/ConfigMonitor.cc @@ -240,9 +240,9 @@ bool ConfigMonitor::preprocess_command(MonOpRequestRef op) } if (!entity.is_client() && !boost::get(&opt->daemon_value)) { - odata.append(stringify(opt->daemon_value)); + odata.append(Option::to_str(opt->daemon_value)); } else { - odata.append(stringify(opt->value)); + odata.append(Option::to_str(opt->value)); } odata.append("\n"); } else { diff --git a/src/test/common/test_config.cc b/src/test/common/test_config.cc index 5b2b62d5068..6e7bd7ccd3e 100644 --- a/src/test/common/test_config.cc +++ b/src/test/common/test_config.cc @@ -153,6 +153,20 @@ TEST(md_config_t, set_val) EXPECT_EQ(-EINVAL, conf.set_val("mgr_osd_bytes", "512 bits", nullptr)); EXPECT_EQ(expected, conf.get_val("mgr_osd_bytes")); } + // set_val should support 1 days 2 hours 4 minutes + { + using namespace std::chrono; + const string s{"1 days 2 hours 4 minutes"}; + using days_t = duration>; + auto expected = (duration_cast(days_t{1}) + + duration_cast(hours{2}) + + duration_cast(minutes{4})); + EXPECT_EQ(0, conf.set_val("mgr_tick_period", + "1 days 2 hours 4 minutes", nullptr)); + EXPECT_EQ(expected.count(), conf.get_val("mgr_tick_period").count()); + EXPECT_EQ(-EINVAL, conf.set_val("mgr_tick_period", "21 centuries", nullptr)); + EXPECT_EQ(expected.count(), conf.get_val("mgr_tick_period").count()); + } } TEST(Option, validation) -- 2.39.5