mirror of
https://github.com/ceph/ceph
synced 2025-03-11 02:39:05 +00:00
Merge PR #25623 into master
* refs/pull/25623/head: common/ceph_time: 'mo' for month common/options: use new parse_timespan common/ceph_time: add parse_timespan common/config_proxy: pass err_ss through on set_val common/ceph_time: add exact_timespan_str Reviewed-by: Kefu Chai <kchai@redhat.com>
This commit is contained in:
commit
8917a153f4
@ -16,6 +16,7 @@
|
||||
#include "ceph_time.h"
|
||||
#include "log/LogClock.h"
|
||||
#include "config.h"
|
||||
#include "strtol.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mach/mach.h>
|
||||
@ -181,4 +182,135 @@ namespace ceph {
|
||||
ss << yr << "y";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string exact_timespan_str(timespan t)
|
||||
{
|
||||
uint64_t nsec = std::chrono::nanoseconds(t).count();
|
||||
uint64_t sec = nsec / 1000000000;
|
||||
nsec %= 1000000000;
|
||||
uint64_t yr = sec / (60 * 60 * 24 * 365);
|
||||
ostringstream ss;
|
||||
if (yr) {
|
||||
ss << yr << "y";
|
||||
sec -= yr * (60 * 60 * 24 * 365);
|
||||
}
|
||||
uint64_t mn = sec / (60 * 60 * 24 * 30);
|
||||
if (mn >= 3) {
|
||||
ss << mn << "mo";
|
||||
sec -= mn * (60 * 60 * 24 * 30);
|
||||
}
|
||||
uint64_t wk = sec / (60 * 60 * 24 * 7);
|
||||
if (wk >= 2) {
|
||||
ss << wk << "w";
|
||||
sec -= wk * (60 * 60 * 24 * 7);
|
||||
}
|
||||
uint64_t day = sec / (60 * 60 * 24);
|
||||
if (day >= 2) {
|
||||
ss << day << "d";
|
||||
sec -= day * (60 * 60 * 24);
|
||||
}
|
||||
uint64_t hr = sec / (60 * 60);
|
||||
if (hr >= 2) {
|
||||
ss << hr << "h";
|
||||
sec -= hr * (60 * 60);
|
||||
}
|
||||
uint64_t min = sec / 60;
|
||||
if (min >= 2) {
|
||||
ss << min << "m";
|
||||
sec -= min * 60;
|
||||
}
|
||||
if (sec) {
|
||||
ss << sec;
|
||||
}
|
||||
if (nsec) {
|
||||
ss << ((float)nsec / 1000000000);
|
||||
}
|
||||
if (sec || nsec) {
|
||||
ss << "s";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::chrono::seconds parse_timespan(const std::string& s)
|
||||
{
|
||||
static std::map<string,int> units = {
|
||||
{ "s", 1 },
|
||||
{ "sec", 1 },
|
||||
{ "second", 1 },
|
||||
{ "seconds", 1 },
|
||||
{ "m", 60 },
|
||||
{ "min", 60 },
|
||||
{ "minute", 60 },
|
||||
{ "minutes", 60 },
|
||||
{ "h", 60*60 },
|
||||
{ "hr", 60*60 },
|
||||
{ "hour", 60*60 },
|
||||
{ "hours", 60*60 },
|
||||
{ "d", 24*60*60 },
|
||||
{ "day", 24*60*60 },
|
||||
{ "days", 24*60*60 },
|
||||
{ "w", 7*24*60*60 },
|
||||
{ "wk", 7*24*60*60 },
|
||||
{ "week", 7*24*60*60 },
|
||||
{ "weeks", 7*24*60*60 },
|
||||
{ "mo", 30*24*60*60 },
|
||||
{ "month", 30*24*60*60 },
|
||||
{ "months", 30*24*60*60 },
|
||||
{ "y", 365*24*60*60 },
|
||||
{ "yr", 365*24*60*60 },
|
||||
{ "year", 365*24*60*60 },
|
||||
{ "years", 365*24*60*60 },
|
||||
};
|
||||
|
||||
auto r = 0s;
|
||||
auto pos = 0u;
|
||||
while (pos < s.size()) {
|
||||
// skip whitespace
|
||||
while (std::isspace(s[pos])) {
|
||||
++pos;
|
||||
}
|
||||
if (pos >= s.size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// consume any digits
|
||||
auto val_start = pos;
|
||||
while (std::isdigit(s[pos])) {
|
||||
++pos;
|
||||
}
|
||||
if (val_start == pos) {
|
||||
throw invalid_argument("expected digit");
|
||||
}
|
||||
string n = s.substr(val_start, pos - val_start);
|
||||
string err;
|
||||
auto val = strict_strtoll(n.c_str(), 10, &err);
|
||||
if (err.size()) {
|
||||
throw invalid_argument(err);
|
||||
}
|
||||
|
||||
// skip whitespace
|
||||
while (std::isspace(s[pos])) {
|
||||
++pos;
|
||||
}
|
||||
|
||||
// consume unit
|
||||
auto unit_start = pos;
|
||||
while (std::isalpha(s[pos])) {
|
||||
++pos;
|
||||
}
|
||||
if (unit_start != pos) {
|
||||
string unit = s.substr(unit_start, pos - unit_start);
|
||||
auto p = units.find(unit);
|
||||
if (p == units.end()) {
|
||||
throw invalid_argument("unrecogized unit '"s + unit + "'");
|
||||
}
|
||||
val *= p->second;
|
||||
} else if (pos < s.size()) {
|
||||
throw invalid_argument("unexpected trailing '"s + s.substr(pos) + "'");
|
||||
}
|
||||
r += chrono::seconds(val);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -487,6 +487,8 @@ inline timespan to_timespan(signedspan z) {
|
||||
}
|
||||
|
||||
std::string timespan_str(timespan t);
|
||||
std::string exact_timespan_str(timespan t);
|
||||
std::chrono::seconds parse_timespan(const std::string& s);
|
||||
|
||||
// detects presence of Clock::to_timespec() and from_timespec()
|
||||
template <typename Clock, typename = std::void_t<>>
|
||||
|
@ -266,7 +266,7 @@ public:
|
||||
int set_val(const std::string& key, const std::string& s,
|
||||
std::stringstream* err_ss=nullptr) {
|
||||
std::lock_guard l{lock};
|
||||
return config.set_val(values, obs_mgr, key, s);
|
||||
return config.set_val(values, obs_mgr, key, s, err_ss);
|
||||
}
|
||||
void set_val_default(const std::string& key, const std::string& val) {
|
||||
std::lock_guard l{lock};
|
||||
|
@ -124,72 +124,6 @@ int Option::validate(const Option::value_t &new_value, std::string *err) const
|
||||
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,
|
||||
@ -262,8 +196,9 @@ int Option::parse_value(
|
||||
*out = sz;
|
||||
} else if (type == Option::TYPE_SECS) {
|
||||
try {
|
||||
*out = parse_duration(val);
|
||||
} catch (const invalid_argument&) {
|
||||
*out = parse_timespan(val);
|
||||
} catch (const invalid_argument& e) {
|
||||
*error_message = e.what();
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
|
@ -159,14 +159,84 @@ TEST(md_config_t, set_val)
|
||||
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}));
|
||||
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));
|
||||
"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());
|
||||
}
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
using days_t = duration<int, std::ratio<3600 * 24>>;
|
||||
|
||||
struct testcase {
|
||||
std::string s;
|
||||
std::chrono::seconds r;
|
||||
};
|
||||
std::vector<testcase> good = {
|
||||
{ "23"s, duration_cast<seconds>(seconds{23}) },
|
||||
{ " 23 "s, duration_cast<seconds>(seconds{23}) },
|
||||
{ " 23s "s, duration_cast<seconds>(seconds{23}) },
|
||||
{ " 23 s "s, duration_cast<seconds>(seconds{23}) },
|
||||
{ " 23 sec "s, duration_cast<seconds>(seconds{23}) },
|
||||
{ "23 second "s, duration_cast<seconds>(seconds{23}) },
|
||||
{ "23 seconds"s, duration_cast<seconds>(seconds{23}) },
|
||||
{ "2m5s"s, duration_cast<seconds>(seconds{2*60+5}) },
|
||||
{ "2 m 5 s "s, duration_cast<seconds>(seconds{2*60+5}) },
|
||||
{ "2 m5"s, duration_cast<seconds>(seconds{2*60+5}) },
|
||||
{ "2 min5"s, duration_cast<seconds>(seconds{2*60+5}) },
|
||||
{ "2 minutes 5"s, duration_cast<seconds>(seconds{2*60+5}) },
|
||||
{ "1w"s, duration_cast<seconds>(seconds{3600*24*7}) },
|
||||
{ "1wk"s, duration_cast<seconds>(seconds{3600*24*7}) },
|
||||
{ "1week"s, duration_cast<seconds>(seconds{3600*24*7}) },
|
||||
{ "1weeks"s, duration_cast<seconds>(seconds{3600*24*7}) },
|
||||
{ "1month"s, duration_cast<seconds>(seconds{3600*24*30}) },
|
||||
{ "1months"s, duration_cast<seconds>(seconds{3600*24*30}) },
|
||||
{ "1mo"s, duration_cast<seconds>(seconds{3600*24*30}) },
|
||||
{ "1y"s, duration_cast<seconds>(seconds{3600*24*365}) },
|
||||
{ "1yr"s, duration_cast<seconds>(seconds{3600*24*365}) },
|
||||
{ "1year"s, duration_cast<seconds>(seconds{3600*24*365}) },
|
||||
{ "1years"s, duration_cast<seconds>(seconds{3600*24*365}) },
|
||||
{ "1d2h3m4s"s,
|
||||
duration_cast<seconds>(days_t{1}) +
|
||||
duration_cast<seconds>(hours{2}) +
|
||||
duration_cast<seconds>(minutes{3}) +
|
||||
duration_cast<seconds>(seconds{4}) },
|
||||
{ "1 days 2 hours 4 minutes"s,
|
||||
duration_cast<seconds>(days_t{1}) +
|
||||
duration_cast<seconds>(hours{2}) +
|
||||
duration_cast<seconds>(minutes{4}) },
|
||||
};
|
||||
|
||||
for (auto& i : good) {
|
||||
cout << "good: " << i.s << " -> " << i.r.count() << std::endl;
|
||||
EXPECT_EQ(0, conf.set_val("mgr_tick_period", i.s, nullptr));
|
||||
EXPECT_EQ(i.r.count(), conf.get_val<seconds>("mgr_tick_period").count());
|
||||
}
|
||||
|
||||
std::vector<std::string> bad = {
|
||||
"12x",
|
||||
"_ 12",
|
||||
"1 2",
|
||||
"21 centuries",
|
||||
"1 y m",
|
||||
};
|
||||
for (auto& i : bad) {
|
||||
std::stringstream err;
|
||||
EXPECT_EQ(-EINVAL, conf.set_val("mgr_tick_period", i, &err));
|
||||
cout << "bad: " << i << " -> " << err.str() << std::endl;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
std::chrono::seconds j = std::chrono::seconds(rand());
|
||||
string s = exact_timespan_str(j);
|
||||
std::chrono::seconds k = parse_timespan(s);
|
||||
cout << "rt: " << j.count() << " -> " << s << " -> " << k.count() << std::endl;
|
||||
EXPECT_EQ(j.count(), k.count());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Option, validation)
|
||||
|
Loading…
Reference in New Issue
Block a user