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);
}
}
}
// 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);
} else if (const double *v = boost::get<const double>(&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);
}
}
// rbd feature validation
#include "librbd/Features.h"
+namespace {
+class printer : public boost::static_visitor<> {
+ ostream& out;
+public:
+ explicit printer(ostream& os)
+ : out(os) {}
+ template<typename T>
+ 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
if (boost::get<boost::blank>(&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<uint64_t>(v));
- } else if (type == TYPE_INT) {
- f->dump_int(field_name, boost::get<int64_t>(v));
- } else if (type == TYPE_STR) {
- f->dump_string(field_name, boost::get<std::string>(v));
- } else if (type == TYPE_FLOAT) {
- f->dump_float(field_name, boost::get<double>(v));
- } else if (type == TYPE_BOOL) {
- f->dump_bool(field_name, boost::get<bool>(v));
- } else if (type == TYPE_SIZE) {
- auto bytes = boost::get<size_t>(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<int64_t>(v)); break;
+ case TYPE_UINT:
+ f->dump_unsigned(field_name, boost::get<uint64_t>(v)); break;
+ case TYPE_STR:
+ f->dump_string(field_name, boost::get<std::string>(v)); break;
+ case TYPE_FLOAT:
+ f->dump_float(field_name, boost::get<double>(v)); break;
+ case TYPE_BOOL:
+ f->dump_bool(field_name, boost::get<bool>(v)); break;
+ default:
+ f->dump_stream(field_name) << v; break;
}
}
return 0;
}
+namespace {
+template<class Duration>
+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<chrono::seconds>(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<int, std::ratio<3600 * 24>>;
+ 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_t>("days", v, start, &new_start);
+ delta.count()) {
+ start = new_start;
+ secs += delta;
+ }
+ if (auto delta = do_parse_duration<hours>("hours", v, start, &new_start);
+ delta.count()) {
+ start = new_start;
+ secs += delta;
+ }
+ if (auto delta = do_parse_duration<minutes>("minutes", v, start, &new_start);
+ delta.count()) {
+ start = new_start;
+ secs += delta;
+ }
+ if (auto delta = do_parse_duration<seconds>("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,
return -EINVAL;
}
*out = sz;
+ } else if (type == Option::TYPE_SECS) {
+ try {
+ *out = parse_duration(val);
+ } catch (const invalid_argument&) {
+ return -EINVAL;
+ }
} else {
ceph_abort();
}
#pragma once
+#include <chrono>
#include <string>
#include <vector>
#include <boost/variant.hpp>
TYPE_ADDR,
TYPE_UUID,
TYPE_SIZE,
+ TYPE_SECS,
};
const char *type_to_str(type_t t) const {
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";
}
}
double,
bool,
entity_addr_t,
+ std::chrono::seconds,
size_t,
uuid_d>;
const std::string name;
value = uuid_d(); break;
case TYPE_SIZE:
value = size_t{0}; break;
+ case TYPE_SECS:
+ value = std::chrono::seconds{0}; break;
default:
ceph_abort();
}
v = bool(new_value); break;
case TYPE_SIZE:
v = size_t{static_cast<std::size_t>(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;
(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);
EXPECT_EQ(-EINVAL, conf.set_val("mgr_osd_bytes", "512 bits", nullptr));
EXPECT_EQ(expected, conf.get_val<Option::size_t>("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<int, std::ratio<3600 * 24>>;
+ auto expected = (duration_cast<seconds>(days_t{1}) +
+ duration_cast<seconds>(hours{2}) +
+ duration_cast<seconds>(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<seconds>("mgr_tick_period").count());
+ EXPECT_EQ(-EINVAL, conf.set_val("mgr_tick_period", "21 centuries", nullptr));
+ EXPECT_EQ(expected.count(), conf.get_val<seconds>("mgr_tick_period").count());
+ }
}
TEST(Option, validation)