From: Rafi Einstein <
ra...@scylladb.com>
Patch is enabled by the following:
- Custom app_template::configuration_reader to handle initial Scylla configuration,
- Seastar configuration items defined in db::config (using UsedFromSeastar category),
- Seastar program_options::variable_map config items are synched with Scylla db::config items
prior to Seastar initialization via app.run_deprecated(),
- Enabled reading config_file configuration items from text form.
This patch is preceded by a patch to Seastar to allow access to app_template::get_default_configuration_reader(),
and followed by a patch to enable configuration reloading via SIGHUP.
---
db/config.hh | 58 +++++++++++++
utils/config_file.hh | 41 +++++++--
utils/config_file_impl.hh | 23 +++--
db/config.cc | 19 +++-
main.cc | 56 +++++++++---
utils/config_file.cc | 216 ++++++++++++++++++++++++++++++++++++++++++----
6 files changed, 370 insertions(+), 43 deletions(-)
diff --git a/db/config.hh b/db/config.hh
index 698ddd88b..9e9451551 100644
--- a/db/config.hh
+++ b/db/config.hh
@@ -55,6 +55,8 @@ struct seed_provider_type {
}
sstring class_name;
std::unordered_map<sstring, sstring> parameters;
+
+ friend std::istream& operator>>(std::istream& is, db::seed_provider_type& self);
};
class config : public utils::config_file {
@@ -739,6 +741,62 @@ class config : public utils::config_file {
" Performance is affected to some extent as a result. Useful to help debugging problems that may arise at another layers.") \
val(cpu_scheduler, bool, true, Used, "Enable cpu scheduling") \
val(view_building, bool, true, Used, "Enable view building; should only be set to false when the node is experience issues due to view building") \
+ \
+ /* Seastar */ \
+ \
+ val(metrics_hostname, sstring, /**/, UsedFromSeastar, "set the hostname used by the metrics, if not set, the local hostname will be used") \
+ val(network_stack, sstring, /**/, UsedFromSeastar, "network stack") \
+ val(no_handle_interrupt, bool, false, UsedFromSeastar, "ignore SIGINT (for gdb)") \
+ val(poll_mode, bool, false, UsedFromSeastar, "poll continuously (100% cpu use)") \
+ val(idle_poll_time_us, unsigned, 200, UsedFromSeastar, "idle polling time in microseconds (reduce for overprovisioned environments or laptops)") \
+ val(poll_aio, bool, true, UsedFromSeastar, "busy-poll for disk I/O (reduces latency and increases throughput)") \
+ val(task_quota_ms, double, 0.5, UsedFromSeastar, "Max time (ms) between polls") \
+ val(max_task_backlog, unsigned, 1000, UsedFromSeastar, "Maximum number of task backlog to allow; above this we ignore I/O") \
+ val(blocked_reactor_notify_ms, unsigned, 2000, UsedFromSeastar, "threshold in miliseconds over which the reactor is considered blocked if no progress is made") \
+ val(blocked_reactor_reports_per_minute, unsigned, 5, UsedFromSeastar, "Maximum number of backtraces reported by stall detector per minute") \
+ val(relaxed_dma, bool, false, UsedFromSeastar, "allow using buffered I/O if DMA is not available (reduces performance)") \
+ val(unsafe_bypass_fsync, bool, false, UsedFromSeastar, "Bypass fsync(, may result in data loss. Use for testing on consumer drives") \
+ val(overprovisioned, bool, false, UsedFromSeastar, "run in an overprovisioned environment (such as docker or a laptop); equivalent to --idle-poll-time-us 0 --thread-affinity 0 --poll-aio 0") \
+ val(abort_on_seastar_bad_alloc, bool, false, UsedFromSeastar, "abort when seastar allocator cannot allocate memory") \
+ \
+ val(smp, unsigned, /**/, UsedFromSeastar, "number of threads (default: one per CPU)") \
+ val(cpuset, sstring, /**/, UsedFromSeastar, "CPUs to use (in cpuset(7) format; default: all))") \
+ val(memory, sstring, /**/, UsedFromSeastar, "memory to use, in bytes (ex: 4G) (default: all)") \
+ val(reserve_memory, sstring, /**/, UsedFromSeastar, "memory reserved to OS (if --memory not specified)") \
+ val(hugepages, sstring, /**/, UsedFromSeastar, "path to accessible hugetlbfs mount (typically /dev/hugepages/something)") \
+ val(lock_memory, bool, /**/, UsedFromSeastar, "lock all memory (prevents swapping)") \
+ val(thread_affinity, bool, true, UsedFromSeastar, "pin threads to their cpus (disable for overprovisioning)") \
+ val(num_io_queues, unsigned, /**/, UsedFromSeastar, "Number of IO queues. Each IO unit will be responsible for a fraction of the IO requests. Defaults to the number of threads") \
+ val(max_io_requests, unsigned, /**/, UsedFromSeastar, "Maximum amount of concurrent requests to be sent to the disk. Defaults to 128 times the number of IO queues") \
+ val(io_properties_file, sstring, /**/, UsedFromSeastar, "path to a YAML file describing the chraracteristics of the I/O Subsystem") \
+ val(io_properties, sstring, /**/, UsedFromSeastar, "a YAML string describing the chraracteristics of the I/O Subsystem") \
+ val(mbind, bool, true, UsedFromSeastar, "enable mbind") \
+ val(enable_glibc_exception_scaling_workaround, bool, true, UsedFromSeastar, "enable workaround for glibc/gcc c++ exception scalablity problem") \
+ \
+ val(collectd, bool, false, UsedFromSeastar, "enable collectd daemon") \
+ val(collectd_address, sstring, "
239.192.74.66:25826", UsedFromSeastar, "address to send/broadcast metrics to") \
+ val(collectd_poll_period, unsigned, 1000, UsedFromSeastar, "poll period - frequency of sending counter metrics (default: 1000ms, 0 disables)") \
+ val(collectd_hostname, sstring, "", UsedFromSeastar, "Deprecated option, use metrics-hostname instead") \
+ \
+ val(dpdk_port_index, unsigned, 0, UsedFromSeastar, "DPDK Port Index") \
+ val(hw_fc, sstring, "on", UsedFromSeastar, "Enable HW Flow Control (on / off)") \
+ \
+ val(tap_device, sstring, "tap0", UsedFromSeastar, "tap device to connect to") \
+ val(host_ipv4_addr, sstring, "192.168.122.2", UsedFromSeastar, "static IPv4 address to use") \
+ val(gw_ipv4_addr, sstring, "192.168.122.1", UsedFromSeastar, "static IPv4 gateway to use") \
+ val(netmask_ipv4_addr, sstring, "255.255.255.0", UsedFromSeastar, "static IPv4 netmask to use") \
+ val(udpv4_queue_size, int, /* ipv4_udp::default_queue_size */, UsedFromSeastar, "Default size of the UDPv4 per-channel packet queue") \
+ val(dhcp, bool, true, UsedFromSeastar, "Use DHCP discovery") \
+ val(hw_queue_weight, float, 1.0f, UsedFromSeastar, "Weighing of a hardware network queue relative to a software queue (0=no work, 1=equal share)") \
+ val(dpdk_pmd, bool, false, UsedFromSeastar, "Use DPDK PMD drivers") \
+ val(lro, sstring, "on", UsedFromSeastar, "Enable LRO") \
+ \
+ val(event_index, sstring, "on", UsedFromSeastar, "Enable event-index feature (on / off)") \
+ val(csum_offload, sstring, "on", UsedFromSeastar, "Enable checksum offload feature (on / off)") \
+ val(tso, sstring, "on", UsedFromSeastar, "Enable TCP segment offload feature (on / off)") \
+ val(ufo, sstring, "on", UsedFromSeastar, "Enable UDP fragmentation offload feature (on / off)") \
+ val(virtio_ring_size, unsigned, 256, UsedFromSeastar, "Virtio ring size (must be power-of-two)") \
+ \
/* done! */
#define _make_value_member(name, type, deflt, status, desc, ...) \
diff --git a/utils/config_file.hh b/utils/config_file.hh
index ee421e167..47f576ede 100644
--- a/utils/config_file.hh
+++ b/utils/config_file.hh
@@ -48,9 +48,10 @@ class config_file {
typedef std::vector<sstring> string_list;
enum class value_status {
- Used,
+ UsedFromSeastar,
+ Used, // note that it is used as pivot in comparisons
Unused,
- Invalid,
+ Invalid
};
enum class config_source : uint8_t {
@@ -61,6 +62,8 @@ class config_file {
struct config_src {
stdx::string_view _name, _desc;
+ sstring _text;
+
public:
config_src(stdx::string_view name, stdx::string_view desc)
: _name(name)
@@ -74,11 +77,16 @@ class config_file {
const stdx::string_view & desc() const {
return _desc;
}
+ virtual sstring text_value() const {
+ return _text;
+ }
virtual void add_command_line_option(
bpo::options_description_easy_init&, const stdx::string_view&,
const stdx::string_view&) = 0;
+ virtual boost::any value() const = 0;
virtual void set_value(const YAML::Node&) = 0;
+ virtual void set_value(sstring) = 0;
virtual value_status status() const = 0;
virtual config_source source() const = 0;
};
@@ -90,6 +98,7 @@ class config_file {
stdx::string_view _name, _desc;
T _value = T();
config_source _source = config_source::None;
+
public:
typedef T type;
typedef named_value<T, S> MyType;
@@ -104,6 +113,9 @@ class config_file {
config_source source() const override {
return _source;
}
+ boost::any value() const override {
+ return _value;
+ }
bool is_set() const {
return _source > config_source::None;
}
@@ -128,6 +140,7 @@ class config_file {
void add_command_line_option(bpo::options_description_easy_init&,
const stdx::string_view&, const stdx::string_view&) override;
void set_value(const YAML::Node&) override;
+ void set_value(sstring) override;
};
typedef std::reference_wrapper<config_src> cfg_ref;
@@ -138,11 +151,10 @@ class config_file {
void add(std::initializer_list<cfg_ref>);
void add(const std::vector<cfg_ref> &);
- boost::program_options::options_description get_options_description();
- boost::program_options::options_description get_options_description(boost::program_options::options_description);
+ bpo::options_description get_options_description();
+ bpo::options_description get_options_description(bpo::options_description);
- boost::program_options::options_description_easy_init&
- add_options(boost::program_options::options_description_easy_init&);
+ bpo::options_description_easy_init& add_options(bpo::options_description_easy_init&);
/**
* Default behaviour for yaml parser is to throw on
@@ -161,6 +173,7 @@ class config_file {
void read_from_yaml(const sstring&, error_handler = {});
void read_from_yaml(const char *, error_handler = {});
+
future<> read_from_file(const sstring&, error_handler = {});
future<> read_from_file(file, error_handler = {});
@@ -171,12 +184,22 @@ class config_file {
const configs& values() const {
return _cfgs;
}
+
+ stdx::optional<cfg_ref> find(sstring name);
+
private:
- configs
- _cfgs;
+ configs _cfgs;
+
+public:
+ void set(sstring name, sstring value);
+
+ void print(sstring title = "Configuation", std::ostream& out = std::cout) const;
+ friend std::ostream& operator<<(std::ostream& out, const config_file::configs& cfg);
+
+ void sync(bpo::variables_map& opts);
};
extern template struct config_file::named_value<seastar::log_level, config_file::value_status::Used>;
-}
+} // namespace utils
diff --git a/utils/config_file_impl.hh b/utils/config_file_impl.hh
index eb81334f1..c807f4b7e 100644
--- a/utils/config_file_impl.hh
+++ b/utils/config_file_impl.hh
@@ -179,14 +179,12 @@ inline typed_value_ex<std::vector<T>>* value_ex(std::vector<T>* v) {
return r;
}
-}
+} // namespace
sstring hyphenate(const stdx::string_view&);
-}
-
template<typename T, utils::config_file::value_status S>
-void utils::config_file::named_value<T, S>::add_command_line_option(
+void config_file::named_value<T, S>::add_command_line_option(
boost::program_options::options_description_easy_init& init,
const stdx::string_view& name, const stdx::string_view& desc) {
// NOTE. We are not adding default values. We could, but must in that case manually (in some way) geenrate the textual
@@ -195,8 +193,21 @@ void utils::config_file::named_value<T, S>::add_command_line_option(
init(hyphenate(name).data(), value_ex(&_value)->notifier([this](auto&&) { _source = config_source::CommandLine; }), desc.data());
}
-template<typename T, utils::config_file::value_status S>
-void utils::config_file::named_value<T, S>::set_value(const YAML::Node& node) {
+template<typename T, config_file::value_status S>
+void config_file::named_value<T, S>::set_value(const YAML::Node& node) {
(*this)(
node.as<T>());
+ if (node.IsScalar()) {
+ _text =
node.as<sstring>();
+ }
+
_source = config_source::SettingsFile;
}
+
+template<typename T, config_file::value_status S>
+void config_file::named_value<T, S>::set_value(sstring val) {
+ (*this)(boost::lexical_cast<T>(val));
+ _text = val;
+ _source = config_source::SettingsFile;
+}
+
+} // namespace utils
diff --git a/db/config.cc b/db/config.cc
index 2f928db96..f7f611e97 100644
--- a/db/config.cc
+++ b/db/config.cc
@@ -80,13 +80,28 @@ struct convert<db::config::seed_provider_type> {
}
+namespace db {
+std::istream& operator>>(std::istream& is, db::seed_provider_type& self)
+{
+ YAML::Node node = YAML::Load(is);
+ YAML::convert<db::config::seed_provider_type>::decode(node, self);
+ return is;
+}
+
+}
+
#define _mk_name(name, ...) name,
#define str(x) #x
#define _mk_init(name, type, deflt, status, desc, ...) , name(str(name), type(deflt), desc)
db::config::config(std::shared_ptr<db::extensions> exts)
- : utils::config_file({ _make_config_values(_mk_name)
- default_log_level, logger_log_level, log_to_stdout, log_to_syslog })
+ : utils::config_file({
+ _make_config_values(_mk_name)
+ default_log_level,
+ logger_log_level,
+ log_to_stdout,
+ log_to_syslog
+ })
_make_config_values(_mk_init)
, default_log_level("default_log_level")
, logger_log_level("logger_log_level")
diff --git a/main.cc b/main.cc
index 0c416d0ea..3d1680140 100644
--- a/main.cc
+++ b/main.cc
@@ -52,6 +52,7 @@
#include <core/file.hh>
#include <sys/time.h>
#include <sys/resource.h>
+#include <boost/filesystem.hpp>
#include "disk-error-handler.hh"
#include "tracing/tracing.hh"
#include "core/prometheus.hh"
@@ -63,6 +64,7 @@
#include "sstables/compaction_manager.hh"
#include "sstables/sstables.hh"
+extern logging::logger configlog;
seastar::metrics::metric_groups app_metrics;
using namespace std::chrono_literals;
@@ -83,29 +85,61 @@ static boost::filesystem::path relative_conf_dir(boost::filesystem::path path) {
return conf_dir / path;
}
-static future<>
-read_config(bpo::variables_map& opts, db::config& cfg) {
+static boost::filesystem::path config_file_path(const bpo::variables_map& opts) {
using namespace boost::filesystem;
- sstring file;
-
- if (opts.count("options-file") > 0) {
- file = opts["options-file"].as<sstring>();
- } else {
- file = relative_conf_dir("scylla.yaml").string();
+ if (! opts.count("options-file")) {
+ return relative_conf_dir("scylla.yaml").string();
}
+ return std::string(opts["options-file"].as<sstring>());
+}
+
+#if 0 // will be back with configuration reloading
+
+static future<>
+read_config_async(boost::filesystem::path config_file, db::config& cfg) {
+ sstring file = config_file.string();
return check_direct_io_support(file).then([file, &cfg] {
return cfg.read_from_file(file, [](auto & opt, auto & msg, auto status) {
auto level = log_level::warn;
if (status.value_or(db::config::value_status::Invalid) != db::config::value_status::Invalid) {
level = log_level::error;
}
- startlog.log(level, "{} : {}", msg, opt);
+ configlog.log(level, "{} : {}", msg, opt);
});
}).handle_exception([file](auto ep) {
- startlog.error("Could not read configuration file {}: {}", file, ep);
+ configlog.error("Could not read configuration file {}: {}", file, ep);
return make_exception_future<>(ep);
});
}
+
+#endif
+
+static void
+read_config(boost::filesystem::path config_file, db::config& cfg) {
+ try {
+ std::ifstream ifs(config_file.string());
+ std::string yaml{std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>()};
+ cfg.read_from_yaml(yaml, [] (auto& opt, auto& msg, auto status) {
+ auto level = log_level::warn;
+ if (status.value_or(db::config::value_status::Invalid) != db::config::value_status::Invalid) {
+ level = log_level::error;
+ }
+ configlog.log(level, "{} : {}", msg, opt);
+ });
+ } catch (std::exception ep) {
+ configlog.error("Could not read configuration file {}: {}", config_file.string(), ep);
+ }
+}
+
+app_template::configuration_reader
+app_config_reader(app_template& app, db::config& db_config) {
+ return [&] (bpo::variables_map& configuration) {
+ app.get_default_configuration_reader()(configuration);
+ read_config(config_file_path(configuration), db_config);
+ db_config.sync(configuration);
+ };
+}
+
static future<> disk_sanity(sstring path, bool developer_mode) {
return check_direct_io_support(path).then([] {
return make_ready_future<>();
@@ -297,6 +331,7 @@ int main(int ac, char** av) {
auto ext = std::make_shared<db::extensions>();
auto cfg = make_lw_shared<db::config>(ext);
+ app.set_configuration_reader(app_config_reader(app, *cfg));
auto init = app.get_options_description().add_options();
// If --version is requested, print it out and exit immediately to avoid
@@ -359,7 +394,6 @@ int main(int ac, char** av) {
tcp_syncookies_sanity();
return seastar::async([cfg, ext, &db, &qp, &proxy, &mm, &ctx, &opts, &dirs, &pctx, &prometheus_server, &return_value, &cf_cache_hitrate_calculator] {
- read_config(opts, *cfg).get();
configurable::init_all(opts, *cfg, *ext).get();
logalloc::prime_segment_pool(memory::stats().total_memory(), memory::min_free_memory()).get();
diff --git a/utils/config_file.cc b/utils/config_file.cc
index c79668288..24f0d8f25 100644
--- a/utils/config_file.cc
+++ b/utils/config_file.cc
@@ -36,9 +36,13 @@
#include <seastar/core/do_with.hh>
#include <seastar/core/print.hh>
+#include "log.hh"
+
#include "config_file.hh"
#include "config_file_impl.hh"
+logging::logger configlog("config");
+
namespace bpo = boost::program_options;
template<>
@@ -197,43 +201,51 @@ std::istream& std::operator>>(std::istream& is, std::vector<seastar::sstring>& r
return is;
}
+
template std::istream& std::operator>>(std::istream&, std::unordered_map<seastar::sstring, seastar::sstring>&);
-sstring utils::hyphenate(const stdx::string_view& v) {
+namespace utils {
+
+sstring hyphenate(const stdx::string_view& v) {
sstring result(v.begin(), v.end());
std::replace(result.begin(), result.end(), '_', '-');
return result;
}
-utils::config_file::config_file(std::initializer_list<cfg_ref> cfgs)
- : _cfgs(cfgs)
+sstring dehyphenate(const stdx::string_view& v) {
+ sstring result(v.begin(), v.end());
+ std::replace(result.begin(), result.end(), '-', '_');
+ return result;
+}
+
+config_file::config_file(std::initializer_list<cfg_ref> cfgs) : _cfgs(cfgs)
{}
-void utils::config_file::add(cfg_ref cfg) {
+void config_file::add(cfg_ref cfg) {
_cfgs.emplace_back(cfg);
}
-void utils::config_file::add(std::initializer_list<cfg_ref> cfgs) {
+void config_file::add(std::initializer_list<cfg_ref> cfgs) {
_cfgs.insert(_cfgs.end(), cfgs.begin(), cfgs.end());
}
-void utils::config_file::add(const std::vector<cfg_ref> & cfgs) {
+void config_file::add(const std::vector<cfg_ref>& cfgs) {
_cfgs.insert(_cfgs.end(), cfgs.begin(), cfgs.end());
}
-bpo::options_description utils::config_file::get_options_description() {
+bpo::options_description config_file::get_options_description() {
bpo::options_description opts("");
return get_options_description(opts);
}
-bpo::options_description utils::config_file::get_options_description(boost::program_options::options_description opts) {
+bpo::options_description config_file::get_options_description(boost::program_options::options_description opts) {
auto init = opts.add_options();
add_options(init);
return std::move(opts);
}
bpo::options_description_easy_init&
-utils::config_file::add_options(bpo::options_description_easy_init& init) {
+config_file::add_options(bpo::options_description_easy_init& init) {
for (config_src& src : _cfgs) {
if (src.status() == value_status::Used) {
auto&& name =
src.name();
@@ -245,11 +257,11 @@ utils::config_file::add_options(bpo::options_description_easy_init& init) {
return init;
}
-void utils::config_file::read_from_yaml(const sstring& yaml, error_handler h) {
+void config_file::read_from_yaml(const sstring& yaml, error_handler h) {
read_from_yaml(yaml.c_str(), std::move(h));
}
-void utils::config_file::read_from_yaml(const char* yaml, error_handler h) {
+void config_file::read_from_yaml(const char* yaml, error_handler h) {
std::unordered_map<sstring, cfg_ref> values;
if (!h) {
@@ -302,13 +314,13 @@ void utils::config_file::read_from_yaml(const char* yaml, error_handler h) {
}
}
-utils::config_file::configs utils::config_file::set_values() const {
+config_file::configs config_file::set_values() const {
return boost::copy_range<configs>(_cfgs | boost::adaptors::filtered([] (const config_src& cfg) {
return cfg.status() > value_status::Used || cfg.source() > config_source::None;
}));
}
-utils::config_file::configs utils::config_file::unset_values() const {
+config_file::configs config_file::unset_values() const {
configs res;
for (config_src& cfg : _cfgs) {
if (cfg.status() > value_status::Used) {
@@ -322,7 +334,7 @@ utils::config_file::configs utils::config_file::unset_values() const {
return res;
}
-future<> utils::config_file::read_from_file(file f, error_handler h) {
+future<> config_file::read_from_file(file f, error_handler h) {
return f.size().then([this, f, h](size_t s) {
return do_with(make_file_input_stream(f), [this, s, h](input_stream<char>& in) {
return in.read_exactly(s).then([this, h](temporary_buffer<char> buf) {
@@ -332,11 +344,185 @@ future<> utils::config_file::read_from_file(file f, error_handler h) {
});
}
-future<> utils::config_file::read_from_file(const sstring& filename, error_handler h) {
+future<> config_file::read_from_file(const sstring& filename, error_handler h) {
return open_file_dma(filename, open_flags::ro).then([this, h](file f) {
return read_from_file(std::move(f), h);
});
}
+stdx::optional<config_file::cfg_ref> config_file::find(sstring name) {
+ for (auto& ci: values()) {
+ auto& c = ci.get();
+ if (
c.name() == name) {
+ return ci;
+ }
+ }
+ return stdx::nullopt;
+}
+void config_file::print(sstring title, std::ostream& out) const {
+ if (!title.empty()) {
+ out << title << ":\n";
+ }
+ out << _cfgs;
+}
+
+std::ostream& operator<<(std::ostream& out, const config_file::configs& cfg) {
+ for (auto& ci: cfg) {
+ auto& c = ci.get();
+ sstring source;
+ switch (c.source()) {
+ case config_file::config_source::None:
+ source = "none";
+ continue;
+ case config_file::config_source::SettingsFile:
+ source = "yaml";
+ break;
+ case config_file::config_source::CommandLine:
+ source = "cmdline";
+ break;
+ };
+ out << "> " <<
c.name() << ": " << source << ": " << c.text_value() << "\n";
+ }
+ out << "---\n";
+ return out;
+}
+
+// boost::any to be re-assigned to its original type
+
+template <class T>
+void any_compat_set(boost::any& a, T&& b) {
+ auto& t = a.type();
+ if (t == typeid(std::string)) {
+ a = boost::lexical_cast<std::string>(b);
+ } else if (t == typeid(seastar::sstring)) {
+ a = boost::lexical_cast<sstring>(b);
+ } else if (t == typeid(int)) {
+ a = boost::lexical_cast<int>(b);
+ } else if (t == typeid(int32_t)) {
+ a = boost::lexical_cast<int32_t>(b);
+ } else if (t == typeid(uint32_t)) {
+ a = boost::lexical_cast<uint32_t>(b);
+ } else if (t == typeid(int64_t)) {
+ a = boost::lexical_cast<int64_t>(b);
+ } else if (t == typeid(unsigned)) {
+ a = boost::lexical_cast<unsigned>(b);
+ } else if (t == typeid(bool)) {
+ a = boost::lexical_cast<bool>(b);
+ } else if (t == typeid(float)) {
+ a = boost::lexical_cast<float>(b);
+ } else if (t == typeid(double)) {
+ a = boost::lexical_cast<double>(b);
+ } else {
+ throw boost::bad_lexical_cast(typeid(b), t);
+ }
+}
+
+static sstring
+to_sstring(const bpo::variable_value& v) {
+ boost::any a = v.value();
+ auto& t = a.type();
+
+ sstring s;
+ if (t == typeid(std::string)) {
+ s =
v.as<std::string>();
+ } else if (t == typeid(seastar::sstring)) {
+ s =
v.as<sstring>();
+ } else if (t == typeid(int)) {
+ s = boost::lexical_cast<std::string>(
v.as<int>());
+ } else if (t == typeid(int32_t)) {
+ s = boost::lexical_cast<std::string>(
v.as<int32_t>());
+ } else if (t == typeid(uint32_t)) {
+ s = boost::lexical_cast<std::string>(
v.as<uint32_t>());
+ } else if (t == typeid(int64_t)) {
+ s = boost::lexical_cast<std::string>(
v.as<int64_t>());
+ } else if (t == typeid(unsigned)) {
+ s = boost::lexical_cast<std::string>(
v.as<unsigned>());
+ } else if (t == typeid(bool)) {
+ s = boost::lexical_cast<std::string>(
v.as<bool>());
+ } else if (t == typeid(float)) {
+ s = boost::lexical_cast<std::string>(
v.as<float>());
+ } else if (t == typeid(double)) {
+ s = boost::lexical_cast<std::string>(
v.as<double>());
+ } else {
+ throw boost::bad_lexical_cast(t, typeid(sstring));
+ }
+
+ return s;
+}
+
+void config_file::sync(bpo::variables_map& opts) {
+ // opts->config: opts should override existing config
+ sstring item_name;
+
+ for (auto opt_i: opts) {
+ try {
+ auto& opt_name = opt_i.first;
+ auto& opt = opt_i.second;
+
+ item_name = opt_name;
+
+ if (opt.empty() || opt.defaulted()) {
+ continue;
+ }
+
+ auto cfg_name{dehyphenate(opt_name)};
+ stdx::optional<cfg_ref> cfg_item = find(cfg_name);
+ if (!cfg_item) {
+ continue;
+ }
+ sstring opt_sval;
+ try {
+ opt_sval = to_sstring(opt);
+ } catch (...) {
+ continue;
+ }
+
+ auto cfg_sval = cfg_item->get().text_value();
+ if (cfg_sval == opt_sval) {
+ continue;
+ }
+
configlog.info("sync opts->yaml: {}={} [was {}]", opt_name, opt_sval, cfg_sval);
+ cfg_item->get().set_value(opt_sval);
+ } catch (...) {
+ configlog.error("sync opt->yaml: problem with {}", item_name);
+ }
+ }
+
+ // config->opts: set if config is present in file (not by default) and opt is missing
+ for (auto& cfg_i: values()) {
+ try {
+ auto& cfg = cfg_i.get();
+
+ sstring cfg_name{
cfg.name()};
+ auto opt_name = hyphenate(cfg_name);
+ item_name = opt_name;
+
+ auto& opt = opts[opt_name];
+ if (! (opt.empty() || opt.defaulted())) {
+ continue;
+ }
+
+ sstring opt_sval;
+ try {
+ opt_sval = to_sstring(opt);
+ } catch (...) {}
+
+ if (cfg.source() == config_source::SettingsFile && cfg.status() == value_status::UsedFromSeastar) {
+ auto cfg_sval = cfg.text_value();
+ if (cfg_sval != opt_sval) {
+
configlog.info("sync yaml->opts: {}={} [was {}]", cfg_name, cfg_sval, opt_sval);
+ try {
+ any_compat_set(
opts.at(std::string(opt_name)).value(), cfg_sval);
+ } catch (...) {
+ opts.insert(std::make_pair(opt_name, bpo::variable_value(cfg.value(), false)));
+ }
+ }
+ }
+ } catch (...) {
+ configlog.error("sync yaml->opt: problem with {}", item_name);
+ }
+ }
+}
+} // namespace utils
--
2.14.4