]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
common/config: specialize the settings for time duration
authorKefu Chai <kchai@redhat.com>
Tue, 13 Feb 2018 08:36:35 +0000 (16:36 +0800)
committerKefu Chai <kchai@redhat.com>
Fri, 9 Mar 2018 05:01:05 +0000 (13:01 +0800)
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 <kchai@redhat.com>
src/common/config.cc
src/common/options.cc
src/common/options.h
src/mon/ConfigMonitor.cc
src/test/common/test_config.cc

index b0912f90ae921903502af03360842f0d492c5547..51ee54a80c0168d65efa5751cbe7fdbc6671c77d 100644 (file)
@@ -290,14 +290,14 @@ int md_config_t::set_mon_vals(CephContext *cct, const map<string,string>& 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<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);
   }
 }
 
index 2d6b957b52be7b9e6c9df0d53bc5054c8ae62064..1588d364d3700360a02b6fd81629562345060dbe 100644 (file)
 // 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
@@ -24,21 +57,21 @@ void Option::dump_value(const char *field_name,
   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;
   }
 }
 
@@ -91,6 +124,72 @@ 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,
@@ -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();
   }
index 8504ba891d1af5940d238814824ce2564df37475..7643ea8397b45e03b0b4bb12c37b2039b349e6a2 100644 (file)
@@ -3,6 +3,7 @@
 
 #pragma once
 
+#include <chrono>
 #include <string>
 #include <vector>
 #include <boost/variant.hpp>
@@ -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<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;
@@ -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);
index 6614919662f6340affb300b42594cd374bef3c20..ef02643b073d85c7dce190ba44a51112ca137562 100644 (file)
@@ -240,9 +240,9 @@ bool ConfigMonitor::preprocess_command(MonOpRequestRef op)
       }
       if (!entity.is_client() &&
          !boost::get<boost::blank>(&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 {
index 5b2b62d5068e08c12feda1e6c33321df584e2d02..6e7bd7ccd3ede9d6ee28087447a541233e3a8489 100644 (file)
@@ -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<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)