1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
6 #include <seastar/core/reactor.hh>
7 #include <seastar/core/sharded.hh>
8 #include "common/config.h"
9 #include "common/config_obs.h"
10 #include "common/config_obs_mgr.h"
11 #include "common/errno.h"
17 namespace crimson::common
{
19 // a facade for managing config. each shard has its own copy of ConfigProxy.
21 // In seastar-osd, there could be multiple instances of @c ConfigValues in a
22 // single process, as we are using a variant of read-copy-update mechinary to
23 // update the settings at runtime.
24 class ConfigProxy
: public seastar::peering_sharded_service
<ConfigProxy
>
26 using LocalConfigValues
= seastar::lw_shared_ptr
<ConfigValues
>;
27 seastar::foreign_ptr
<LocalConfigValues
> values
;
29 md_config_t
* remote_config
= nullptr;
30 std::unique_ptr
<md_config_t
> local_config
;
32 using ConfigObserver
= ceph::md_config_obs_impl
<ConfigProxy
>;
33 ObserverMgr
<ConfigObserver
> obs_mgr
;
35 const md_config_t
& get_config() const {
36 return remote_config
? *remote_config
: * local_config
;
38 md_config_t
& get_config() {
39 return remote_config
? *remote_config
: * local_config
;
42 // apply changes to all shards
43 // @param func a functor which accepts @c "ConfigValues&"
44 template<typename Func
>
45 seastar::future
<> do_change(Func
&& func
) {
46 return container().invoke_on(values
.get_owner_shard(),
47 [func
= std::move(func
)](ConfigProxy
& owner
) {
48 // apply the changes to a copy of the values
49 auto new_values
= seastar::make_lw_shared(*owner
.values
);
50 new_values
->changed
.clear();
53 // always apply the new settings synchronously on the owner shard, to
54 // avoid racings with other do_change() calls in parallel.
55 ObserverMgr
<ConfigObserver
>::rev_obs_map rev_obs
;
56 owner
.values
.reset(new_values
);
57 owner
.obs_mgr
.for_each_change(owner
.values
->changed
, owner
,
58 [&rev_obs
](ConfigObserver
*obs
,
59 const std::string
&key
) {
60 rev_obs
[obs
].insert(key
);
62 for (auto& [obs
, keys
] : rev_obs
) {
63 obs
->handle_conf_change(owner
, keys
);
66 return seastar::parallel_for_each(boost::irange(1u, seastar::smp::count
),
67 [&owner
, new_values
] (auto cpu
) {
68 return owner
.container().invoke_on(cpu
,
69 [foreign_values
= seastar::make_foreign(new_values
)](ConfigProxy
& proxy
) mutable {
71 proxy
.values
= std::move(foreign_values
);
73 ObserverMgr
<ConfigObserver
>::rev_obs_map rev_obs
;
74 proxy
.obs_mgr
.for_each_change(proxy
.values
->changed
, proxy
,
75 [&rev_obs
](ConfigObserver
*obs
, const std::string
& key
) {
76 rev_obs
[obs
].insert(key
);
78 for (auto& obs_keys
: rev_obs
) {
79 obs_keys
.first
->handle_conf_change(proxy
, obs_keys
.second
);
82 }).finally([new_values
] {
83 new_values
->changed
.clear();
88 ConfigProxy(const EntityName
& name
, std::string_view cluster
);
89 const ConfigValues
* operator->() const noexcept
{
92 const ConfigValues
get_config_values() {
95 ConfigValues
* operator->() noexcept
{
99 // required by sharded<>
100 seastar::future
<> start();
101 seastar::future
<> stop() {
102 return seastar::make_ready_future
<>();
104 void add_observer(ConfigObserver
* obs
) {
105 obs_mgr
.add_observer(obs
);
107 void remove_observer(ConfigObserver
* obs
) {
108 obs_mgr
.remove_observer(obs
);
110 seastar::future
<> rm_val(const std::string
& key
) {
111 return do_change([key
, this](ConfigValues
& values
) {
112 auto ret
= get_config().rm_val(values
, key
);
114 throw std::invalid_argument(cpp_strerror(ret
));
118 seastar::future
<> set_val(const std::string
& key
,
119 const std::string
& val
) {
120 return do_change([key
, val
, this](ConfigValues
& values
) {
121 std::stringstream err
;
122 auto ret
= get_config().set_val(values
, obs_mgr
, key
, val
, &err
);
124 throw std::invalid_argument(err
.str());
128 int get_val(const std::string
&key
, std::string
*val
) const {
129 return get_config().get_val(*values
, key
, val
);
132 const T
get_val(const std::string
& key
) const {
133 return get_config().template get_val
<T
>(*values
, key
);
136 int get_all_sections(std::vector
<std::string
>& sections
) const {
137 return get_config().get_all_sections(sections
);
140 int get_val_from_conf_file(const std::vector
<std::string
>& sections
,
141 const std::string
& key
, std::string
& out
,
142 bool expand_meta
) const {
143 return get_config().get_val_from_conf_file(*values
, sections
, key
,
147 unsigned get_osd_pool_default_min_size(uint8_t size
) const {
148 return get_config().get_osd_pool_default_min_size(*values
, size
);
152 set_mon_vals(const std::map
<std::string
,std::string
,std::less
<>>& kv
) {
153 return do_change([kv
, this](ConfigValues
& values
) {
154 get_config().set_mon_vals(nullptr, values
, obs_mgr
, kv
, nullptr);
158 void show_config(ceph::Formatter
* f
) const;
160 seastar::future
<> parse_argv(std::vector
<const char*>& argv
) {
161 // we could pass whatever is unparsed to seastar, but seastar::app_template
162 // is used for driving the seastar application, and
163 // crimson::common::ConfigProxy is not available until seastar engine is up
164 // and running, so we have to feed the command line args to app_template
165 // first, then pass them to ConfigProxy.
166 return do_change([&argv
, this](ConfigValues
& values
) {
167 get_config().parse_argv(values
,
174 seastar::future
<> parse_config_files(const std::string
& conf_files
) {
175 return do_change([this, conf_files
](ConfigValues
& values
) {
176 const char* conf_file_paths
=
177 conf_files
.empty() ? nullptr : conf_files
.c_str();
178 get_config().parse_config_files(values
,
182 CODE_ENVIRONMENT_DAEMON
);
186 using ShardedConfig
= seastar::sharded
<ConfigProxy
>;
189 static ShardedConfig sharded_conf
;
190 friend ConfigProxy
& local_conf();
191 friend ShardedConfig
& sharded_conf();
194 inline ConfigProxy
& local_conf() {
195 return ConfigProxy::sharded_conf
.local();
198 inline ConfigProxy::ShardedConfig
& sharded_conf() {
199 return ConfigProxy::sharded_conf
;