]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
11fdf7f2 TL |
15 | #include <boost/type_traits.hpp> |
16 | ||
7c673cae FG |
17 | #include "common/ceph_argparse.h" |
18 | #include "common/common_init.h" | |
19 | #include "common/config.h" | |
11fdf7f2 | 20 | #include "common/config_obs.h" |
7c673cae | 21 | #include "include/str_list.h" |
7c673cae | 22 | #include "include/stringify.h" |
7c673cae FG |
23 | #include "osd/osd_types.h" |
24 | #include "common/errno.h" | |
25 | #include "common/hostname.h" | |
11fdf7f2 | 26 | #include "common/dout.h" |
7c673cae FG |
27 | |
28 | /* Don't use standard Ceph logging in this file. | |
29 | * We can't use logging until it's initialized, and a lot of the necessary | |
30 | * initialization happens here. | |
31 | */ | |
32 | #undef dout | |
7c673cae FG |
33 | #undef pdout |
34 | #undef derr | |
7c673cae | 35 | #undef generic_dout |
11fdf7f2 TL |
36 | |
37 | // set set_mon_vals() | |
38 | #define dout_subsys ceph_subsys_monc | |
7c673cae FG |
39 | |
40 | using std::map; | |
41 | using std::list; | |
7c673cae FG |
42 | using std::ostringstream; |
43 | using std::pair; | |
7c673cae FG |
44 | using std::string; |
45 | ||
11fdf7f2 | 46 | static const char *CEPH_CONF_FILE_DEFAULT = "$data_dir/config, /etc/ceph/$cluster.conf, $home/.ceph/$cluster.conf, $cluster.conf" |
7c673cae FG |
47 | #if defined(__FreeBSD__) |
48 | ", /usr/local/etc/ceph/$cluster.conf" | |
49 | #endif | |
50 | ; | |
51 | ||
52 | #define _STR(x) #x | |
53 | #define STRINGIFY(x) _STR(x) | |
54 | ||
11fdf7f2 TL |
55 | const char *ceph_conf_level_name(int level) |
56 | { | |
57 | switch (level) { | |
58 | case CONF_DEFAULT: return "default"; // built-in default | |
59 | case CONF_MON: return "mon"; // monitor config database | |
60 | case CONF_ENV: return "env"; // process environment (CEPH_ARGS) | |
61 | case CONF_FILE: return "file"; // ceph.conf file | |
62 | case CONF_CMDLINE: return "cmdline"; // process command line args | |
63 | case CONF_OVERRIDE: return "override"; // injectargs or 'config set' at runtime | |
64 | case CONF_FINAL: return "final"; | |
65 | default: return "???"; | |
66 | } | |
67 | } | |
68 | ||
7c673cae FG |
69 | int ceph_resolve_file_search(const std::string& filename_list, |
70 | std::string& result) | |
71 | { | |
72 | list<string> ls; | |
73 | get_str_list(filename_list, ls); | |
74 | ||
75 | int ret = -ENOENT; | |
76 | list<string>::iterator iter; | |
77 | for (iter = ls.begin(); iter != ls.end(); ++iter) { | |
91327a77 | 78 | int fd = ::open(iter->c_str(), O_RDONLY|O_CLOEXEC); |
7c673cae FG |
79 | if (fd < 0) { |
80 | ret = -errno; | |
81 | continue; | |
82 | } | |
83 | close(fd); | |
84 | result = *iter; | |
85 | return 0; | |
86 | } | |
87 | ||
88 | return ret; | |
89 | } | |
90 | ||
11fdf7f2 | 91 | static int conf_stringify(const Option::value_t& v, string *out) |
c07f9fc5 | 92 | { |
11fdf7f2 TL |
93 | if (boost::get<boost::blank>(&v)) { |
94 | return -ENOENT; | |
95 | } | |
96 | *out = Option::to_str(v); | |
97 | return 0; | |
98 | } | |
c07f9fc5 | 99 | |
11fdf7f2 TL |
100 | md_config_t::md_config_t(ConfigValues& values, |
101 | const ConfigTracker& tracker, | |
102 | bool is_daemon) | |
103 | : is_daemon(is_daemon) | |
104 | { | |
c07f9fc5 FG |
105 | // Load the compile-time list of Option into |
106 | // a map so that we can resolve keys quickly. | |
107 | for (const auto &i : ceph_options) { | |
108 | if (schema.count(i.name)) { | |
109 | // We may be instantiated pre-logging so send | |
110 | std::cerr << "Duplicate config key in schema: '" << i.name << "'" | |
111 | << std::endl; | |
11fdf7f2 | 112 | ceph_abort(); |
c07f9fc5 | 113 | } |
9f95a23c | 114 | schema.emplace(i.name, i); |
11fdf7f2 TL |
115 | } |
116 | ||
117 | // Define the debug_* options as well. | |
118 | subsys_options.reserve(values.subsys.get_num()); | |
119 | for (unsigned i = 0; i < values.subsys.get_num(); ++i) { | |
120 | string name = string("debug_") + values.subsys.get_name(i); | |
121 | subsys_options.push_back( | |
122 | Option(name, Option::TYPE_STR, Option::LEVEL_ADVANCED)); | |
123 | Option& opt = subsys_options.back(); | |
124 | opt.set_default(stringify(values.subsys.get_log_level(i)) + "/" + | |
125 | stringify(values.subsys.get_gather_level(i))); | |
126 | string desc = string("Debug level for ") + values.subsys.get_name(i); | |
127 | opt.set_description(desc.c_str()); | |
128 | opt.set_flag(Option::FLAG_RUNTIME); | |
129 | opt.set_long_description("The value takes the form 'N' or 'N/M' where N and M are values between 0 and 99. N is the debug level to log (all values below this are included), and M is the level to gather and buffer in memory. In the event of a crash, the most recent items <= M are dumped to the log file."); | |
130 | opt.set_subsys(i); | |
131 | opt.set_validator([](std::string *value, std::string *error_message) { | |
132 | int m, n; | |
133 | int r = sscanf(value->c_str(), "%d/%d", &m, &n); | |
134 | if (r >= 1) { | |
135 | if (m < 0 || m > 99) { | |
136 | *error_message = "value must be in range [0, 99]"; | |
137 | return -ERANGE; | |
138 | } | |
139 | if (r == 2) { | |
140 | if (n < 0 || n > 99) { | |
141 | *error_message = "value must be in range [0, 99]"; | |
142 | return -ERANGE; | |
143 | } | |
144 | } else { | |
145 | // normalize to M/N | |
146 | n = m; | |
147 | *value = stringify(m) + "/" + stringify(n); | |
148 | } | |
149 | } else { | |
150 | *error_message = "value must take the form N or N/M, where N and M are integers"; | |
151 | return -EINVAL; | |
152 | } | |
153 | return 0; | |
154 | }); | |
155 | } | |
156 | for (auto& opt : subsys_options) { | |
157 | schema.emplace(opt.name, opt); | |
c07f9fc5 FG |
158 | } |
159 | ||
160 | // Populate list of legacy_values according to the OPTION() definitions | |
161 | // Note that this is just setting up our map of name->member ptr. The | |
162 | // default values etc will get loaded in along with new-style data, | |
163 | // as all loads write to both the values map, and the legacy | |
164 | // members if present. | |
165 | legacy_values = { | |
166 | #define OPTION(name, type) \ | |
9f95a23c | 167 | {STRINGIFY(name), &ConfigValues::name}, |
c07f9fc5 FG |
168 | #define SAFE_OPTION(name, type) OPTION(name, type) |
169 | #include "common/legacy_config_opts.h" | |
7c673cae | 170 | #undef OPTION |
7c673cae | 171 | #undef SAFE_OPTION |
c07f9fc5 | 172 | }; |
7c673cae | 173 | |
c07f9fc5 | 174 | validate_schema(); |
7c673cae | 175 | |
11fdf7f2 | 176 | // Validate default values from the schema |
c07f9fc5 FG |
177 | for (const auto &i : schema) { |
178 | const Option &opt = i.second; | |
c07f9fc5 | 179 | if (opt.type == Option::TYPE_STR) { |
11fdf7f2 TL |
180 | bool has_daemon_default = !boost::get<boost::blank>(&opt.daemon_value); |
181 | Option::value_t default_val; | |
182 | if (is_daemon && has_daemon_default) { | |
183 | default_val = opt.daemon_value; | |
184 | } else { | |
185 | default_val = opt.value; | |
186 | } | |
c07f9fc5 FG |
187 | // We call pre_validate as a sanity check, but also to get any |
188 | // side effect (value modification) from the validator. | |
189 | std::string *def_str = boost::get<std::string>(&default_val); | |
11fdf7f2 | 190 | std::string val = *def_str; |
c07f9fc5 | 191 | std::string err; |
11fdf7f2 | 192 | if (opt.pre_validate(&val, &err) != 0) { |
c07f9fc5 FG |
193 | std::cerr << "Default value " << opt.name << "=" << *def_str << " is " |
194 | "invalid: " << err << std::endl; | |
195 | ||
196 | // This is the compiled-in default that is failing its own option's | |
197 | // validation, so this is super-invalid and should never make it | |
198 | // past a pull request: crash out. | |
11fdf7f2 TL |
199 | ceph_abort(); |
200 | } | |
201 | if (val != *def_str) { | |
202 | // if the validator normalizes the string into a different form than | |
203 | // what was compiled in, use that. | |
204 | set_val_default(values, tracker, opt.name, val); | |
c07f9fc5 FG |
205 | } |
206 | } | |
c07f9fc5 | 207 | } |
7c673cae | 208 | |
c07f9fc5 | 209 | // Copy out values (defaults) into any legacy (C struct member) fields |
11fdf7f2 TL |
210 | update_legacy_vals(values); |
211 | } | |
7c673cae | 212 | |
11fdf7f2 TL |
213 | md_config_t::~md_config_t() |
214 | { | |
c07f9fc5 FG |
215 | } |
216 | ||
217 | /** | |
218 | * Sanity check schema. Assert out on failures, to ensure any bad changes | |
219 | * cannot possibly pass any testing and make it into a release. | |
220 | */ | |
221 | void md_config_t::validate_schema() | |
7c673cae | 222 | { |
c07f9fc5 FG |
223 | for (const auto &i : schema) { |
224 | const auto &opt = i.second; | |
225 | for (const auto &see_also_key : opt.see_also) { | |
226 | if (schema.count(see_also_key) == 0) { | |
227 | std::cerr << "Non-existent see-also key '" << see_also_key | |
228 | << "' on option '" << opt.name << "'" << std::endl; | |
11fdf7f2 | 229 | ceph_abort(); |
c07f9fc5 FG |
230 | } |
231 | } | |
232 | } | |
7c673cae | 233 | |
c07f9fc5 FG |
234 | for (const auto &i : legacy_values) { |
235 | if (schema.count(i.first) == 0) { | |
236 | std::cerr << "Schema is missing legacy field '" << i.first << "'" | |
237 | << std::endl; | |
11fdf7f2 | 238 | ceph_abort(); |
c07f9fc5 FG |
239 | } |
240 | } | |
7c673cae FG |
241 | } |
242 | ||
9f95a23c | 243 | const Option *md_config_t::find_option(const std::string_view name) const |
7c673cae | 244 | { |
11fdf7f2 TL |
245 | auto p = schema.find(name); |
246 | if (p != schema.end()) { | |
247 | return &p->second; | |
248 | } | |
249 | return nullptr; | |
7c673cae FG |
250 | } |
251 | ||
11fdf7f2 TL |
252 | void md_config_t::set_val_default(ConfigValues& values, |
253 | const ConfigTracker& tracker, | |
9f95a23c | 254 | const string_view name, const std::string& val) |
7c673cae | 255 | { |
11fdf7f2 TL |
256 | const Option *o = find_option(name); |
257 | ceph_assert(o); | |
258 | string err; | |
259 | int r = _set_val(values, tracker, val, *o, CONF_DEFAULT, &err); | |
260 | ceph_assert(r >= 0); | |
7c673cae FG |
261 | } |
262 | ||
11fdf7f2 TL |
263 | int md_config_t::set_mon_vals(CephContext *cct, |
264 | ConfigValues& values, | |
265 | const ConfigTracker& tracker, | |
9f95a23c | 266 | const map<string,string,less<>>& kv, |
11fdf7f2 | 267 | config_callback config_cb) |
7c673cae | 268 | { |
11fdf7f2 | 269 | ignored_mon_values.clear(); |
f64942e4 | 270 | |
11fdf7f2 TL |
271 | if (!config_cb) { |
272 | ldout(cct, 4) << __func__ << " no callback set" << dendl; | |
273 | } | |
f64942e4 | 274 | |
11fdf7f2 TL |
275 | for (auto& i : kv) { |
276 | if (config_cb) { | |
277 | if (config_cb(i.first, i.second)) { | |
278 | ldout(cct, 4) << __func__ << " callback consumed " << i.first << dendl; | |
279 | continue; | |
280 | } | |
281 | ldout(cct, 4) << __func__ << " callback ignored " << i.first << dendl; | |
7c673cae | 282 | } |
11fdf7f2 TL |
283 | const Option *o = find_option(i.first); |
284 | if (!o) { | |
285 | ldout(cct,10) << __func__ << " " << i.first << " = " << i.second | |
286 | << " (unrecognized option)" << dendl; | |
287 | continue; | |
288 | } | |
289 | if (o->has_flag(Option::FLAG_NO_MON_UPDATE)) { | |
290 | ignored_mon_values.emplace(i); | |
291 | continue; | |
292 | } | |
293 | std::string err; | |
294 | int r = _set_val(values, tracker, i.second, *o, CONF_MON, &err); | |
295 | if (r < 0) { | |
9f95a23c TL |
296 | ldout(cct, 4) << __func__ << " failed to set " << i.first << " = " |
297 | << i.second << ": " << err << dendl; | |
11fdf7f2 TL |
298 | ignored_mon_values.emplace(i); |
299 | } else if (r == ConfigValues::SET_NO_CHANGE || | |
300 | r == ConfigValues::SET_NO_EFFECT) { | |
301 | ldout(cct,20) << __func__ << " " << i.first << " = " << i.second | |
302 | << " (no change)" << dendl; | |
303 | } else if (r == ConfigValues::SET_HAVE_EFFECT) { | |
304 | ldout(cct,10) << __func__ << " " << i.first << " = " << i.second << dendl; | |
305 | } else { | |
306 | ceph_abort(); | |
7c673cae FG |
307 | } |
308 | } | |
11fdf7f2 TL |
309 | values.for_each([&] (auto name, auto configs) { |
310 | auto config = configs.find(CONF_MON); | |
311 | if (config == configs.end()) { | |
312 | return; | |
313 | } | |
314 | if (kv.find(name) != kv.end()) { | |
315 | return; | |
316 | } | |
317 | ldout(cct,10) << __func__ << " " << name | |
318 | << " cleared (was " << Option::to_str(config->second) << ")" | |
319 | << dendl; | |
320 | values.rm_val(name, CONF_MON); | |
92f5a8d4 TL |
321 | // if this is a debug option, it needs to propagate to teh subsys; |
322 | // this isn't covered by update_legacy_vals() below. similarly, | |
323 | // we want to trigger a config notification for these items. | |
324 | const Option *o = find_option(name); | |
325 | _refresh(values, *o); | |
11fdf7f2 TL |
326 | }); |
327 | values_bl.clear(); | |
328 | update_legacy_vals(values); | |
329 | return 0; | |
7c673cae FG |
330 | } |
331 | ||
11fdf7f2 TL |
332 | int md_config_t::parse_config_files(ConfigValues& values, |
333 | const ConfigTracker& tracker, | |
334 | const char *conf_files_str, | |
7c673cae FG |
335 | std::ostream *warnings, |
336 | int flags) | |
337 | { | |
7c673cae | 338 | |
11fdf7f2 | 339 | if (safe_to_start_threads) |
7c673cae FG |
340 | return -ENOSYS; |
341 | ||
11fdf7f2 | 342 | if (!values.cluster.size() && !conf_files_str) { |
7c673cae FG |
343 | /* |
344 | * set the cluster name to 'ceph' when neither cluster name nor | |
345 | * configuration file are specified. | |
346 | */ | |
11fdf7f2 | 347 | values.cluster = "ceph"; |
7c673cae FG |
348 | } |
349 | ||
11fdf7f2 | 350 | if (!conf_files_str) { |
7c673cae FG |
351 | const char *c = getenv("CEPH_CONF"); |
352 | if (c) { | |
11fdf7f2 | 353 | conf_files_str = c; |
7c673cae FG |
354 | } |
355 | else { | |
356 | if (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE) | |
357 | return 0; | |
11fdf7f2 | 358 | conf_files_str = CEPH_CONF_FILE_DEFAULT; |
7c673cae FG |
359 | } |
360 | } | |
361 | ||
11fdf7f2 TL |
362 | std::list<std::string> conf_files; |
363 | get_str_list(conf_files_str, conf_files); | |
364 | auto p = conf_files.begin(); | |
365 | while (p != conf_files.end()) { | |
7c673cae | 366 | string &s = *p; |
11fdf7f2 TL |
367 | if (s.find("$data_dir") != string::npos && |
368 | data_dir_option.empty()) { | |
369 | // useless $data_dir item, skip | |
370 | p = conf_files.erase(p); | |
7c673cae | 371 | } else { |
11fdf7f2 | 372 | early_expand_meta(values, s, warnings); |
7c673cae FG |
373 | ++p; |
374 | } | |
375 | } | |
7c673cae FG |
376 | |
377 | // open new conf | |
378 | list<string>::const_iterator c; | |
379 | for (c = conf_files.begin(); c != conf_files.end(); ++c) { | |
380 | cf.clear(); | |
381 | string fn = *c; | |
9f95a23c TL |
382 | ostringstream oss; |
383 | int ret = cf.parse_file(fn.c_str(), &oss); | |
384 | parse_error = oss.str(); | |
385 | if (ret == 0) { | |
7c673cae | 386 | break; |
9f95a23c TL |
387 | } |
388 | if (ret != -ENOENT) | |
7c673cae FG |
389 | return ret; |
390 | } | |
391 | // it must have been all ENOENTs, that's the only way we got here | |
392 | if (c == conf_files.end()) | |
393 | return -ENOENT; | |
394 | ||
11fdf7f2 | 395 | if (values.cluster.size() == 0) { |
7c673cae FG |
396 | /* |
397 | * If cluster name is not set yet, use the prefix of the | |
398 | * basename of configuration file as cluster name. | |
399 | */ | |
400 | auto start = c->rfind('/') + 1; | |
401 | auto end = c->find(".conf", start); | |
402 | if (end == c->npos) { | |
403 | /* | |
404 | * If the configuration file does not follow $cluster.conf | |
405 | * convention, we do the last try and assign the cluster to | |
406 | * 'ceph'. | |
407 | */ | |
11fdf7f2 | 408 | values.cluster = "ceph"; |
7c673cae | 409 | } else { |
11fdf7f2 | 410 | values.cluster = c->substr(start, end - start); |
7c673cae FG |
411 | } |
412 | } | |
413 | ||
414 | std::vector <std::string> my_sections; | |
11fdf7f2 | 415 | _get_my_sections(values, my_sections); |
c07f9fc5 FG |
416 | for (const auto &i : schema) { |
417 | const auto &opt = i.second; | |
7c673cae | 418 | std::string val; |
11fdf7f2 | 419 | int ret = _get_val_from_conf_file(my_sections, opt.name, val); |
7c673cae FG |
420 | if (ret == 0) { |
421 | std::string error_message; | |
11fdf7f2 TL |
422 | int r = _set_val(values, tracker, val, opt, CONF_FILE, &error_message); |
423 | if (warnings != nullptr && (r < 0 || !error_message.empty())) { | |
7c673cae FG |
424 | *warnings << "parse error setting '" << opt.name << "' to '" << val |
425 | << "'"; | |
426 | if (!error_message.empty()) { | |
427 | *warnings << " (" << error_message << ")"; | |
428 | } | |
429 | *warnings << std::endl; | |
430 | } | |
431 | } | |
432 | } | |
433 | ||
7c673cae FG |
434 | // Warn about section names that look like old-style section names |
435 | std::deque < std::string > old_style_section_names; | |
9f95a23c TL |
436 | for (auto& [name, section] : cf) { |
437 | if (((name.find("mds") == 0) || (name.find("mon") == 0) || | |
438 | (name.find("osd") == 0)) && (name.size() > 3) && (name[3] != '.')) { | |
439 | old_style_section_names.push_back(name); | |
7c673cae FG |
440 | } |
441 | } | |
442 | if (!old_style_section_names.empty()) { | |
443 | ostringstream oss; | |
444 | cerr << "ERROR! old-style section name(s) found: "; | |
445 | string sep; | |
446 | for (std::deque < std::string >::const_iterator os = old_style_section_names.begin(); | |
447 | os != old_style_section_names.end(); ++os) { | |
448 | cerr << sep << *os; | |
449 | sep = ", "; | |
450 | } | |
451 | cerr << ". Please use the new style section names that include a period."; | |
452 | } | |
11fdf7f2 TL |
453 | |
454 | update_legacy_vals(values); | |
455 | ||
7c673cae FG |
456 | return 0; |
457 | } | |
458 | ||
11fdf7f2 TL |
459 | void md_config_t::parse_env(unsigned entity_type, |
460 | ConfigValues& values, | |
461 | const ConfigTracker& tracker, | |
462 | const char *args_var) | |
7c673cae | 463 | { |
11fdf7f2 | 464 | if (safe_to_start_threads) |
7c673cae | 465 | return; |
11fdf7f2 TL |
466 | if (!args_var) { |
467 | args_var = "CEPH_ARGS"; | |
468 | } | |
81eedcae TL |
469 | if (auto s = getenv("CEPH_KEYRING"); s) { |
470 | string err; | |
471 | _set_val(values, tracker, s, *find_option("keyring"), CONF_ENV, &err); | |
11fdf7f2 | 472 | } |
81eedcae | 473 | if (auto dir = getenv("CEPH_LIB"); dir) { |
11fdf7f2 TL |
474 | for (auto name : { "erasure_code_dir", "plugin_dir", "osd_class_dir" }) { |
475 | std::string err; | |
476 | const Option *o = find_option(name); | |
477 | ceph_assert(o); | |
478 | _set_val(values, tracker, dir, *o, CONF_ENV, &err); | |
479 | } | |
480 | } | |
494da23a TL |
481 | |
482 | // Apply pod memory limits: | |
483 | // | |
484 | // There are two types of resource requests: `limits` and `requests`. | |
485 | // | |
486 | // - Requests: Used by the K8s scheduler to determine on which nodes to | |
487 | // schedule the pods. This helps spread the pods to different nodes. This | |
488 | // value should be conservative in order to make sure all the pods are | |
489 | // schedulable. This corresponds to POD_MEMORY_REQUEST (set by the Rook | |
490 | // CRD) and is the target memory utilization we try to maintain for daemons | |
491 | // that respect it. | |
492 | // | |
493 | // If POD_MEMORY_REQUEST is present, we use it as the target. | |
494 | // | |
495 | // - Limits: At runtime, the container runtime (and Linux) will use the | |
496 | // limits to see if the pod is using too many resources. In that case, the | |
497 | // pod will be killed/restarted automatically if the pod goes over the limit. | |
498 | // This should be higher than what is specified for requests (potentially | |
499 | // much higher). This corresponds to the cgroup memory limit that will | |
500 | // trigger the Linux OOM killer. | |
501 | // | |
502 | // If POD_MEMORY_LIMIT is present, we use it as the /default/ value for | |
503 | // the target, which means it will only apply if the *_memory_target option | |
504 | // isn't set via some other path (e.g., POD_MEMORY_REQUEST, or the cluster | |
505 | // config, or whatever.) | |
506 | // | |
507 | // Here are the documented best practices: | |
508 | // https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/#motivation-for-cpu-requests-and-limits | |
509 | // | |
510 | // When the operator creates the CephCluster CR, it will need to generate the | |
511 | // desired requests and limits. As long as we are conservative in our choice | |
512 | // for requests and generous with the limits we should be in a good place to | |
513 | // get started. | |
514 | // | |
515 | // The support in Rook is already there for applying the limits as seen in | |
516 | // these links. | |
517 | // | |
518 | // Rook docs on the resource requests and limits: | |
519 | // https://rook.io/docs/rook/v1.0/ceph-cluster-crd.html#cluster-wide-resources-configuration-settings | |
520 | // Example CR settings: | |
521 | // https://github.com/rook/rook/blob/6d2ef936698593036185aabcb00d1d74f9c7bfc1/cluster/examples/kubernetes/ceph/cluster.yaml#L90 | |
522 | // | |
523 | uint64_t pod_limit = 0, pod_request = 0; | |
524 | if (auto pod_lim = getenv("POD_MEMORY_LIMIT"); pod_lim) { | |
81eedcae | 525 | string err; |
494da23a | 526 | uint64_t v = atoll(pod_lim); |
11fdf7f2 TL |
527 | if (v) { |
528 | switch (entity_type) { | |
529 | case CEPH_ENTITY_TYPE_OSD: | |
494da23a TL |
530 | { |
531 | double cgroup_ratio = get_val<double>( | |
532 | values, "osd_memory_target_cgroup_limit_ratio"); | |
533 | if (cgroup_ratio > 0.0) { | |
534 | pod_limit = v * cgroup_ratio; | |
535 | // set osd_memory_target *default* based on cgroup limit, so that | |
536 | // it can be overridden by any explicit settings elsewhere. | |
537 | set_val_default(values, tracker, | |
538 | "osd_memory_target", stringify(pod_limit)); | |
539 | } | |
540 | } | |
11fdf7f2 TL |
541 | } |
542 | } | |
543 | } | |
494da23a TL |
544 | if (auto pod_req = getenv("POD_MEMORY_REQUEST"); pod_req) { |
545 | if (uint64_t v = atoll(pod_req); v) { | |
546 | pod_request = v; | |
547 | } | |
548 | } | |
549 | if (pod_request && pod_limit) { | |
550 | // If both LIMIT and REQUEST are set, ensure that we use the | |
551 | // min of request and limit*ratio. This is important | |
552 | // because k8s set set LIMIT == REQUEST if only LIMIT is | |
553 | // specified, and we want to apply the ratio in that case, | |
554 | // even though REQUEST is present. | |
555 | pod_request = std::min<uint64_t>(pod_request, pod_limit); | |
556 | } | |
557 | if (pod_request) { | |
558 | string err; | |
559 | switch (entity_type) { | |
560 | case CEPH_ENTITY_TYPE_OSD: | |
561 | _set_val(values, tracker, stringify(pod_request), | |
562 | *find_option("osd_memory_target"), | |
563 | CONF_ENV, &err); | |
564 | break; | |
565 | } | |
566 | } | |
567 | ||
11fdf7f2 TL |
568 | if (getenv(args_var)) { |
569 | vector<const char *> env_args; | |
570 | env_to_vec(env_args, args_var); | |
571 | parse_argv(values, tracker, env_args, CONF_ENV); | |
7c673cae FG |
572 | } |
573 | } | |
574 | ||
11fdf7f2 TL |
575 | void md_config_t::show_config(const ConfigValues& values, |
576 | std::ostream& out) const | |
7c673cae | 577 | { |
11fdf7f2 | 578 | _show_config(values, &out, nullptr); |
7c673cae FG |
579 | } |
580 | ||
11fdf7f2 TL |
581 | void md_config_t::show_config(const ConfigValues& values, |
582 | Formatter *f) const | |
7c673cae | 583 | { |
11fdf7f2 | 584 | _show_config(values, nullptr, f); |
7c673cae FG |
585 | } |
586 | ||
11fdf7f2 | 587 | void md_config_t::config_options(Formatter *f) const |
1adf2230 | 588 | { |
1adf2230 AA |
589 | f->open_array_section("options"); |
590 | for (const auto& i: schema) { | |
11fdf7f2 | 591 | f->dump_object("option", i.second); |
1adf2230 AA |
592 | } |
593 | f->close_section(); | |
594 | } | |
595 | ||
11fdf7f2 TL |
596 | void md_config_t::_show_config(const ConfigValues& values, |
597 | std::ostream *out, Formatter *f) const | |
7c673cae FG |
598 | { |
599 | if (out) { | |
11fdf7f2 TL |
600 | *out << "name = " << values.name << std::endl; |
601 | *out << "cluster = " << values.cluster << std::endl; | |
7c673cae FG |
602 | } |
603 | if (f) { | |
11fdf7f2 TL |
604 | f->dump_string("name", stringify(values.name)); |
605 | f->dump_string("cluster", values.cluster); | |
7c673cae | 606 | } |
c07f9fc5 FG |
607 | for (const auto& i: schema) { |
608 | const Option &opt = i.second; | |
11fdf7f2 TL |
609 | string val; |
610 | conf_stringify(_get_val(values, opt), &val); | |
611 | if (out) { | |
612 | *out << opt.name << " = " << val << std::endl; | |
613 | } | |
614 | if (f) { | |
615 | f->dump_string(opt.name.c_str(), val); | |
616 | } | |
7c673cae FG |
617 | } |
618 | } | |
619 | ||
11fdf7f2 TL |
620 | int md_config_t::parse_argv(ConfigValues& values, |
621 | const ConfigTracker& tracker, | |
622 | std::vector<const char*>& args, int level) | |
7c673cae | 623 | { |
11fdf7f2 | 624 | if (safe_to_start_threads) { |
7c673cae FG |
625 | return -ENOSYS; |
626 | } | |
627 | ||
7c673cae FG |
628 | // In this function, don't change any parts of the configuration directly. |
629 | // Instead, use set_val to set them. This will allow us to send the proper | |
630 | // observer notifications later. | |
631 | std::string val; | |
632 | for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) { | |
633 | if (strcmp(*i, "--") == 0) { | |
634 | /* Normally we would use ceph_argparse_double_dash. However, in this | |
635 | * function we *don't* want to remove the double dash, because later | |
636 | * argument parses will still need to see it. */ | |
637 | break; | |
638 | } | |
639 | else if (ceph_argparse_flag(args, i, "--show_conf", (char*)NULL)) { | |
640 | cerr << cf << std::endl; | |
641 | _exit(0); | |
642 | } | |
643 | else if (ceph_argparse_flag(args, i, "--show_config", (char*)NULL)) { | |
11fdf7f2 | 644 | do_show_config = true; |
7c673cae FG |
645 | } |
646 | else if (ceph_argparse_witharg(args, i, &val, "--show_config_value", (char*)NULL)) { | |
11fdf7f2 TL |
647 | do_show_config_value = val; |
648 | } | |
649 | else if (ceph_argparse_flag(args, i, "--no-mon-config", (char*)NULL)) { | |
650 | values.no_mon_config = true; | |
651 | } | |
652 | else if (ceph_argparse_flag(args, i, "--log-early", (char*)NULL)) { | |
653 | values.log_early = true; | |
654 | } | |
655 | else if (ceph_argparse_flag(args, i, "--mon-config", (char*)NULL)) { | |
656 | values.no_mon_config = false; | |
7c673cae FG |
657 | } |
658 | else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) { | |
11fdf7f2 | 659 | set_val_or_die(values, tracker, "daemonize", "false"); |
7c673cae FG |
660 | } |
661 | else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) { | |
e306af50 | 662 | set_val_or_die(values, tracker, "fuse_debug", "true"); |
11fdf7f2 TL |
663 | set_val_or_die(values, tracker, "daemonize", "false"); |
664 | set_val_or_die(values, tracker, "log_file", ""); | |
665 | set_val_or_die(values, tracker, "log_to_stderr", "true"); | |
666 | set_val_or_die(values, tracker, "err_to_stderr", "true"); | |
667 | set_val_or_die(values, tracker, "log_to_syslog", "false"); | |
7c673cae FG |
668 | } |
669 | // Some stuff that we wanted to give universal single-character options for | |
670 | // Careful: you can burn through the alphabet pretty quickly by adding | |
671 | // to this list. | |
672 | else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) { | |
11fdf7f2 | 673 | set_val_or_die(values, tracker, "monmap", val.c_str()); |
7c673cae FG |
674 | } |
675 | else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) { | |
11fdf7f2 | 676 | set_val_or_die(values, tracker, "mon_host", val.c_str()); |
7c673cae FG |
677 | } |
678 | else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) { | |
11fdf7f2 | 679 | set_val_or_die(values, tracker, "public_addr", val.c_str()); |
7c673cae FG |
680 | } |
681 | else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) { | |
11fdf7f2 TL |
682 | bufferlist bl; |
683 | string err; | |
684 | int r; | |
685 | if (val == "-") { | |
686 | r = bl.read_fd(STDIN_FILENO, 1024); | |
687 | } else { | |
688 | r = bl.read_file(val.c_str(), &err); | |
689 | } | |
690 | if (r >= 0) { | |
691 | string k(bl.c_str(), bl.length()); | |
692 | set_val_or_die(values, tracker, "key", k.c_str()); | |
693 | } | |
7c673cae FG |
694 | } |
695 | else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) { | |
11fdf7f2 | 696 | set_val_or_die(values, tracker, "keyring", val.c_str()); |
7c673cae FG |
697 | } |
698 | else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) { | |
11fdf7f2 | 699 | set_val_or_die(values, tracker, "client_mountpoint", val.c_str()); |
7c673cae FG |
700 | } |
701 | else { | |
11fdf7f2 | 702 | int r = parse_option(values, tracker, args, i, NULL, level); |
3efd9988 FG |
703 | if (r < 0) { |
704 | return r; | |
705 | } | |
7c673cae FG |
706 | } |
707 | } | |
11fdf7f2 TL |
708 | // meta expands could have modified anything. Copy it all out again. |
709 | update_legacy_vals(values); | |
710 | return 0; | |
711 | } | |
712 | ||
713 | void md_config_t::do_argv_commands(const ConfigValues& values) const | |
714 | { | |
7c673cae | 715 | |
11fdf7f2 TL |
716 | if (do_show_config) { |
717 | _show_config(values, &cout, NULL); | |
7c673cae FG |
718 | _exit(0); |
719 | } | |
720 | ||
11fdf7f2 TL |
721 | if (do_show_config_value.size()) { |
722 | string val; | |
723 | int r = conf_stringify(_get_val(values, do_show_config_value, 0, &cerr), | |
724 | &val); | |
7c673cae FG |
725 | if (r < 0) { |
726 | if (r == -ENOENT) | |
11fdf7f2 TL |
727 | std::cerr << "failed to get config option '" |
728 | << do_show_config_value << "': option not found" << std::endl; | |
7c673cae | 729 | else |
11fdf7f2 TL |
730 | std::cerr << "failed to get config option '" |
731 | << do_show_config_value << "': " << cpp_strerror(r) | |
732 | << std::endl; | |
7c673cae FG |
733 | _exit(1); |
734 | } | |
11fdf7f2 | 735 | std::cout << val << std::endl; |
7c673cae FG |
736 | _exit(0); |
737 | } | |
7c673cae FG |
738 | } |
739 | ||
11fdf7f2 TL |
740 | int md_config_t::parse_option(ConfigValues& values, |
741 | const ConfigTracker& tracker, | |
742 | std::vector<const char*>& args, | |
743 | std::vector<const char*>::iterator& i, | |
744 | ostream *oss, | |
745 | int level) | |
7c673cae FG |
746 | { |
747 | int ret = 0; | |
c07f9fc5 | 748 | size_t o = 0; |
7c673cae FG |
749 | std::string val; |
750 | ||
c07f9fc5 | 751 | std::string option_name; |
7c673cae FG |
752 | std::string error_message; |
753 | o = 0; | |
c07f9fc5 FG |
754 | for (const auto& opt_iter: schema) { |
755 | const Option &opt = opt_iter.second; | |
7c673cae | 756 | ostringstream err; |
7c673cae | 757 | std::string as_option("--"); |
c07f9fc5 FG |
758 | as_option += opt.name; |
759 | option_name = opt.name; | |
11fdf7f2 TL |
760 | if (ceph_argparse_witharg( |
761 | args, i, &val, err, | |
762 | string(string("--default-") + opt.name).c_str(), (char*)NULL)) { | |
763 | if (!err.str().empty()) { | |
764 | error_message = err.str(); | |
765 | ret = -EINVAL; | |
766 | break; | |
767 | } | |
768 | ret = _set_val(values, tracker, val, opt, CONF_DEFAULT, &error_message); | |
769 | break; | |
770 | } else if (opt.type == Option::TYPE_BOOL) { | |
7c673cae FG |
771 | int res; |
772 | if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(), | |
773 | (char*)NULL)) { | |
774 | if (res == 0) | |
11fdf7f2 | 775 | ret = _set_val(values, tracker, "false", opt, level, &error_message); |
7c673cae | 776 | else if (res == 1) |
11fdf7f2 | 777 | ret = _set_val(values, tracker, "true", opt, level, &error_message); |
7c673cae FG |
778 | else |
779 | ret = res; | |
780 | break; | |
781 | } else { | |
782 | std::string no("--no-"); | |
c07f9fc5 | 783 | no += opt.name; |
7c673cae | 784 | if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) { |
11fdf7f2 | 785 | ret = _set_val(values, tracker, "false", opt, level, &error_message); |
7c673cae FG |
786 | break; |
787 | } | |
788 | } | |
789 | } else if (ceph_argparse_witharg(args, i, &val, err, | |
790 | as_option.c_str(), (char*)NULL)) { | |
791 | if (!err.str().empty()) { | |
792 | error_message = err.str(); | |
793 | ret = -EINVAL; | |
794 | break; | |
795 | } | |
11fdf7f2 | 796 | ret = _set_val(values, tracker, val, opt, level, &error_message); |
7c673cae FG |
797 | break; |
798 | } | |
799 | ++o; | |
800 | } | |
801 | ||
11fdf7f2 TL |
802 | if (ret < 0 || !error_message.empty()) { |
803 | ceph_assert(!option_name.empty()); | |
7c673cae FG |
804 | if (oss) { |
805 | *oss << "Parse error setting " << option_name << " to '" | |
806 | << val << "' using injectargs"; | |
807 | if (!error_message.empty()) { | |
808 | *oss << " (" << error_message << ")"; | |
809 | } | |
810 | *oss << ".\n"; | |
811 | } else { | |
812 | cerr << "parse error setting '" << option_name << "' to '" | |
813 | << val << "'"; | |
814 | if (!error_message.empty()) { | |
815 | cerr << " (" << error_message << ")"; | |
816 | } | |
817 | cerr << "\n" << std::endl; | |
818 | } | |
819 | } | |
820 | ||
c07f9fc5 | 821 | if (o == schema.size()) { |
7c673cae FG |
822 | // ignore |
823 | ++i; | |
824 | } | |
11fdf7f2 | 825 | return ret >= 0 ? 0 : ret; |
7c673cae FG |
826 | } |
827 | ||
11fdf7f2 TL |
828 | int md_config_t::parse_injectargs(ConfigValues& values, |
829 | const ConfigTracker& tracker, | |
830 | std::vector<const char*>& args, | |
7c673cae FG |
831 | std::ostream *oss) |
832 | { | |
7c673cae FG |
833 | int ret = 0; |
834 | for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) { | |
11fdf7f2 | 835 | int r = parse_option(values, tracker, args, i, oss, CONF_OVERRIDE); |
7c673cae FG |
836 | if (r < 0) |
837 | ret = r; | |
838 | } | |
839 | return ret; | |
840 | } | |
841 | ||
11fdf7f2 | 842 | void md_config_t::set_safe_to_start_threads() |
7c673cae | 843 | { |
11fdf7f2 | 844 | safe_to_start_threads = true; |
7c673cae FG |
845 | } |
846 | ||
11fdf7f2 | 847 | void md_config_t::_clear_safe_to_start_threads() |
7c673cae | 848 | { |
11fdf7f2 | 849 | safe_to_start_threads = false; |
7c673cae FG |
850 | } |
851 | ||
11fdf7f2 TL |
852 | int md_config_t::injectargs(ConfigValues& values, |
853 | const ConfigTracker& tracker, | |
854 | const std::string& s, std::ostream *oss) | |
7c673cae FG |
855 | { |
856 | int ret; | |
11fdf7f2 TL |
857 | char b[s.length()+1]; |
858 | strcpy(b, s.c_str()); | |
859 | std::vector<const char*> nargs; | |
860 | char *p = b; | |
861 | while (*p) { | |
862 | nargs.push_back(p); | |
863 | while (*p && *p != ' ') p++; | |
864 | if (!*p) | |
865 | break; | |
866 | *p++ = 0; | |
867 | while (*p && *p == ' ') p++; | |
868 | } | |
869 | ret = parse_injectargs(values, tracker, nargs, oss); | |
870 | if (!nargs.empty()) { | |
871 | *oss << " failed to parse arguments: "; | |
872 | std::string prefix; | |
873 | for (std::vector<const char*>::const_iterator i = nargs.begin(); | |
874 | i != nargs.end(); ++i) { | |
875 | *oss << prefix << *i; | |
876 | prefix = ","; | |
7c673cae | 877 | } |
11fdf7f2 TL |
878 | *oss << "\n"; |
879 | ret = -EINVAL; | |
7c673cae | 880 | } |
11fdf7f2 | 881 | update_legacy_vals(values); |
7c673cae FG |
882 | return ret; |
883 | } | |
884 | ||
11fdf7f2 TL |
885 | void md_config_t::set_val_or_die(ConfigValues& values, |
886 | const ConfigTracker& tracker, | |
9f95a23c | 887 | const std::string_view key, |
11fdf7f2 | 888 | const std::string &val) |
7c673cae | 889 | { |
c07f9fc5 | 890 | std::stringstream err; |
11fdf7f2 | 891 | int ret = set_val(values, tracker, key, val, &err); |
c07f9fc5 FG |
892 | if (ret != 0) { |
893 | std::cerr << "set_val_or_die(" << key << "): " << err.str(); | |
7c673cae | 894 | } |
11fdf7f2 | 895 | ceph_assert(ret == 0); |
7c673cae FG |
896 | } |
897 | ||
11fdf7f2 TL |
898 | int md_config_t::set_val(ConfigValues& values, |
899 | const ConfigTracker& tracker, | |
9f95a23c | 900 | const std::string_view key, const char *val, |
11fdf7f2 | 901 | std::stringstream *err_ss) |
7c673cae | 902 | { |
c07f9fc5 FG |
903 | if (key.empty()) { |
904 | if (err_ss) *err_ss << "No key specified"; | |
7c673cae | 905 | return -EINVAL; |
c07f9fc5 FG |
906 | } |
907 | if (!val) { | |
7c673cae | 908 | return -EINVAL; |
c07f9fc5 | 909 | } |
7c673cae FG |
910 | |
911 | std::string v(val); | |
7c673cae FG |
912 | |
913 | string k(ConfFile::normalize_key_name(key)); | |
914 | ||
c07f9fc5 FG |
915 | const auto &opt_iter = schema.find(k); |
916 | if (opt_iter != schema.end()) { | |
917 | const Option &opt = opt_iter->second; | |
7c673cae | 918 | std::string error_message; |
11fdf7f2 TL |
919 | int r = _set_val(values, tracker, v, opt, CONF_OVERRIDE, &error_message); |
920 | if (r >= 0) { | |
c07f9fc5 | 921 | if (err_ss) *err_ss << "Set " << opt.name << " to " << v; |
11fdf7f2 | 922 | r = 0; |
c07f9fc5 FG |
923 | } else { |
924 | if (err_ss) *err_ss << error_message; | |
925 | } | |
7c673cae FG |
926 | return r; |
927 | } | |
928 | ||
c07f9fc5 | 929 | if (err_ss) *err_ss << "Configuration option not found: '" << key << "'"; |
7c673cae FG |
930 | return -ENOENT; |
931 | } | |
932 | ||
9f95a23c | 933 | int md_config_t::rm_val(ConfigValues& values, const std::string_view key) |
11fdf7f2 TL |
934 | { |
935 | return _rm_val(values, key, CONF_OVERRIDE); | |
936 | } | |
937 | ||
938 | void md_config_t::get_defaults_bl(const ConfigValues& values, | |
939 | bufferlist *bl) | |
940 | { | |
941 | if (defaults_bl.length() == 0) { | |
942 | uint32_t n = 0; | |
943 | bufferlist bl; | |
944 | for (const auto &i : schema) { | |
945 | ++n; | |
946 | encode(i.second.name, bl); | |
947 | auto [value, found] = values.get_value(i.second.name, CONF_DEFAULT); | |
948 | if (found) { | |
949 | encode(Option::to_str(value), bl); | |
950 | } else { | |
951 | string val; | |
952 | conf_stringify(_get_val_default(i.second), &val); | |
953 | encode(val, bl); | |
954 | } | |
955 | } | |
956 | encode(n, defaults_bl); | |
957 | defaults_bl.claim_append(bl); | |
958 | } | |
959 | *bl = defaults_bl; | |
960 | } | |
961 | ||
962 | void md_config_t::get_config_bl( | |
963 | const ConfigValues& values, | |
964 | uint64_t have_version, | |
965 | bufferlist *bl, | |
966 | uint64_t *got_version) | |
967 | { | |
968 | if (values_bl.length() == 0) { | |
969 | uint32_t n = 0; | |
970 | bufferlist bl; | |
971 | values.for_each([&](auto& name, auto& configs) { | |
972 | if (name == "fsid" || | |
973 | name == "host") { | |
974 | return; | |
975 | } | |
976 | ++n; | |
977 | encode(name, bl); | |
978 | encode((uint32_t)configs.size(), bl); | |
979 | for (auto& j : configs) { | |
980 | encode(j.first, bl); | |
981 | encode(Option::to_str(j.second), bl); | |
982 | } | |
983 | }); | |
984 | // make sure overridden items appear, and include the default value | |
985 | for (auto& i : ignored_mon_values) { | |
986 | if (values.contains(i.first)) { | |
987 | continue; | |
988 | } | |
989 | if (i.first == "fsid" || | |
990 | i.first == "host") { | |
991 | continue; | |
992 | } | |
993 | const Option *opt = find_option(i.first); | |
994 | if (!opt) { | |
995 | continue; | |
996 | } | |
997 | ++n; | |
998 | encode(i.first, bl); | |
999 | encode((uint32_t)1, bl); | |
1000 | encode((int32_t)CONF_DEFAULT, bl); | |
1001 | string val; | |
1002 | conf_stringify(_get_val_default(*opt), &val); | |
1003 | encode(val, bl); | |
1004 | } | |
1005 | encode(n, values_bl); | |
1006 | values_bl.claim_append(bl); | |
1007 | encode(ignored_mon_values, values_bl); | |
1008 | ++values_bl_version; | |
1009 | } | |
1010 | if (have_version != values_bl_version) { | |
1011 | *bl = values_bl; | |
1012 | *got_version = values_bl_version; | |
1013 | } | |
1014 | } | |
7c673cae | 1015 | |
11fdf7f2 | 1016 | int md_config_t::get_val(const ConfigValues& values, |
9f95a23c | 1017 | const std::string_view key, char **buf, int len) const |
7c673cae | 1018 | { |
11fdf7f2 TL |
1019 | string k(ConfFile::normalize_key_name(key)); |
1020 | return _get_val_cstr(values, k, buf, len); | |
7c673cae FG |
1021 | } |
1022 | ||
11fdf7f2 TL |
1023 | int md_config_t::get_val( |
1024 | const ConfigValues& values, | |
9f95a23c | 1025 | const std::string_view key, |
11fdf7f2 | 1026 | std::string *val) const |
7c673cae | 1027 | { |
11fdf7f2 | 1028 | return conf_stringify(get_val_generic(values, key), val); |
7c673cae FG |
1029 | } |
1030 | ||
11fdf7f2 TL |
1031 | Option::value_t md_config_t::get_val_generic( |
1032 | const ConfigValues& values, | |
9f95a23c | 1033 | const std::string_view key) const |
7c673cae | 1034 | { |
9f95a23c | 1035 | return _get_val(values, key); |
11fdf7f2 | 1036 | } |
7c673cae | 1037 | |
11fdf7f2 TL |
1038 | Option::value_t md_config_t::_get_val( |
1039 | const ConfigValues& values, | |
9f95a23c | 1040 | const std::string_view key, |
11fdf7f2 TL |
1041 | expand_stack_t *stack, |
1042 | std::ostream *err) const | |
1043 | { | |
c07f9fc5 FG |
1044 | if (key.empty()) { |
1045 | return Option::value_t(boost::blank()); | |
1046 | } | |
7c673cae FG |
1047 | |
1048 | // In key names, leading and trailing whitespace are not significant. | |
1049 | string k(ConfFile::normalize_key_name(key)); | |
1050 | ||
9f95a23c | 1051 | const Option *o = find_option(k); |
11fdf7f2 TL |
1052 | if (!o) { |
1053 | // not a valid config option | |
c07f9fc5 | 1054 | return Option::value_t(boost::blank()); |
7c673cae | 1055 | } |
11fdf7f2 TL |
1056 | |
1057 | return _get_val(values, *o, stack, err); | |
7c673cae FG |
1058 | } |
1059 | ||
11fdf7f2 TL |
1060 | Option::value_t md_config_t::_get_val( |
1061 | const ConfigValues& values, | |
1062 | const Option& o, | |
1063 | expand_stack_t *stack, | |
1064 | std::ostream *err) const | |
1065 | { | |
1066 | expand_stack_t a_stack; | |
1067 | if (!stack) { | |
1068 | stack = &a_stack; | |
1069 | } | |
1070 | return _expand_meta(values, | |
1071 | _get_val_nometa(values, o), | |
1072 | &o, stack, err); | |
1073 | } | |
7c673cae | 1074 | |
11fdf7f2 TL |
1075 | Option::value_t md_config_t::_get_val_nometa(const ConfigValues& values, |
1076 | const Option& o) const | |
1077 | { | |
1078 | if (auto [value, found] = values.get_value(o.name, -1); found) { | |
1079 | return value; | |
1080 | } else { | |
1081 | return _get_val_default(o); | |
1082 | } | |
1083 | } | |
1084 | ||
1085 | const Option::value_t& md_config_t::_get_val_default(const Option& o) const | |
1086 | { | |
1087 | bool has_daemon_default = !boost::get<boost::blank>(&o.daemon_value); | |
1088 | if (is_daemon && has_daemon_default) { | |
1089 | return o.daemon_value; | |
1090 | } else { | |
1091 | return o.value; | |
1092 | } | |
1093 | } | |
1094 | ||
1095 | void md_config_t::early_expand_meta( | |
1096 | const ConfigValues& values, | |
1097 | std::string &val, | |
1098 | std::ostream *err) const | |
1099 | { | |
1100 | expand_stack_t stack; | |
1101 | Option::value_t v = _expand_meta(values, | |
1102 | Option::value_t(val), | |
1103 | nullptr, &stack, err); | |
1104 | conf_stringify(v, &val); | |
1105 | } | |
1106 | ||
1107 | bool md_config_t::finalize_reexpand_meta(ConfigValues& values, | |
1108 | const ConfigTracker& tracker) | |
1109 | { | |
adb31ebb TL |
1110 | std::vector<std::string> reexpands; |
1111 | reexpands.swap(may_reexpand_meta); | |
1112 | for (auto& name : reexpands) { | |
1113 | // always refresh the options if they are in the may_reexpand_meta | |
1114 | // map, because the options may have already been expanded with old | |
1115 | // meta. | |
1116 | const auto &opt_iter = schema.find(name); | |
1117 | ceph_assert(opt_iter != schema.end()); | |
1118 | const Option &opt = opt_iter->second; | |
1119 | _refresh(values, opt); | |
11fdf7f2 | 1120 | } |
adb31ebb TL |
1121 | |
1122 | return !may_reexpand_meta.empty(); | |
11fdf7f2 TL |
1123 | } |
1124 | ||
1125 | Option::value_t md_config_t::_expand_meta( | |
1126 | const ConfigValues& values, | |
1127 | const Option::value_t& in, | |
1128 | const Option *o, | |
1129 | expand_stack_t *stack, | |
1130 | std::ostream *err) const | |
1131 | { | |
1132 | //cout << __func__ << " in '" << in << "' stack " << stack << std::endl; | |
1133 | if (!stack) { | |
1134 | return in; | |
1135 | } | |
1136 | const std::string *str = boost::get<const std::string>(&in); | |
1137 | if (!str) { | |
1138 | // strings only! | |
1139 | return in; | |
1140 | } | |
1141 | ||
1142 | auto pos = str->find('$'); | |
1143 | if (pos == std::string::npos) { | |
1144 | // no substitutions! | |
1145 | return in; | |
1146 | } | |
1147 | ||
1148 | if (o) { | |
1149 | stack->push_back(make_pair(o, &in)); | |
1150 | } | |
1151 | string out; | |
1152 | decltype(pos) last_pos = 0; | |
1153 | while (pos != std::string::npos) { | |
1154 | ceph_assert((*str)[pos] == '$'); | |
1155 | if (pos > last_pos) { | |
1156 | out += str->substr(last_pos, pos - last_pos); | |
1157 | } | |
1158 | ||
1159 | // try to parse the variable name into var, either \$\{(.+)\} or | |
1160 | // \$[a-z\_]+ | |
1161 | const char *valid_chars = "abcdefghijklmnopqrstuvwxyz_"; | |
1162 | string var; | |
1163 | size_t endpos = 0; | |
1164 | if ((*str)[pos+1] == '{') { | |
1165 | // ...${foo_bar}... | |
1166 | endpos = str->find_first_not_of(valid_chars, pos + 2); | |
1167 | if (endpos != std::string::npos && | |
1168 | (*str)[endpos] == '}') { | |
1169 | var = str->substr(pos + 2, endpos - pos - 2); | |
1170 | endpos++; | |
1171 | } | |
7c673cae | 1172 | } else { |
11fdf7f2 TL |
1173 | // ...$foo... |
1174 | endpos = str->find_first_not_of(valid_chars, pos + 1); | |
1175 | if (endpos != std::string::npos) | |
1176 | var = str->substr(pos + 1, endpos - pos - 1); | |
1177 | else | |
1178 | var = str->substr(pos + 1); | |
7c673cae | 1179 | } |
11fdf7f2 TL |
1180 | last_pos = endpos; |
1181 | ||
1182 | if (!var.size()) { | |
1183 | out += '$'; | |
1184 | } else { | |
1185 | //cout << " found var " << var << std::endl; | |
1186 | // special metavariable? | |
1187 | if (var == "type") { | |
1188 | out += values.name.get_type_name(); | |
1189 | } else if (var == "cluster") { | |
1190 | out += values.cluster; | |
1191 | } else if (var == "name") { | |
1192 | out += values.name.to_cstr(); | |
1193 | } else if (var == "host") { | |
1194 | if (values.host == "") { | |
1195 | out += ceph_get_short_hostname(); | |
1196 | } else { | |
1197 | out += values.host; | |
1198 | } | |
1199 | } else if (var == "num") { | |
1200 | out += values.name.get_id().c_str(); | |
1201 | } else if (var == "id") { | |
1202 | out += values.name.get_id(); | |
1203 | } else if (var == "pid") { | |
1204 | out += stringify(getpid()); | |
1205 | if (o) { | |
adb31ebb | 1206 | may_reexpand_meta.push_back(o->name); |
11fdf7f2 TL |
1207 | } |
1208 | } else if (var == "cctid") { | |
1209 | out += stringify((unsigned long long)this); | |
1210 | } else if (var == "home") { | |
1211 | const char *home = getenv("HOME"); | |
1212 | out = home ? std::string(home) : std::string(); | |
1213 | } else { | |
1214 | if (var == "data_dir") { | |
1215 | var = data_dir_option; | |
1216 | } | |
1217 | const Option *o = find_option(var); | |
1218 | if (!o) { | |
1219 | out += str->substr(pos, endpos - pos); | |
1220 | } else { | |
1221 | auto match = std::find_if( | |
1222 | stack->begin(), stack->end(), | |
1223 | [o](pair<const Option *,const Option::value_t*>& item) { | |
1224 | return item.first == o; | |
1225 | }); | |
1226 | if (match != stack->end()) { | |
1227 | // substitution loop; break the cycle | |
1228 | if (err) { | |
1229 | *err << "variable expansion loop at " << var << "=" | |
1230 | << Option::to_str(*match->second) << "\n" | |
1231 | << "expansion stack:\n"; | |
1232 | for (auto i = stack->rbegin(); i != stack->rend(); ++i) { | |
1233 | *err << i->first->name << "=" | |
1234 | << Option::to_str(*i->second) << "\n"; | |
1235 | } | |
1236 | } | |
1237 | return Option::value_t(std::string("$") + o->name); | |
1238 | } else { | |
1239 | // recursively evaluate! | |
1240 | string n; | |
1241 | conf_stringify(_get_val(values, *o, stack, err), &n); | |
1242 | out += n; | |
1243 | } | |
1244 | } | |
1245 | } | |
1246 | } | |
1247 | pos = str->find('$', last_pos); | |
7c673cae | 1248 | } |
11fdf7f2 TL |
1249 | if (last_pos != std::string::npos) { |
1250 | out += str->substr(last_pos); | |
1251 | } | |
1252 | if (o) { | |
1253 | stack->pop_back(); | |
1254 | } | |
1255 | ||
1256 | return Option::value_t(out); | |
7c673cae FG |
1257 | } |
1258 | ||
11fdf7f2 TL |
1259 | int md_config_t::_get_val_cstr( |
1260 | const ConfigValues& values, | |
9f95a23c | 1261 | const std::string& key, char **buf, int len) const |
7c673cae | 1262 | { |
c07f9fc5 | 1263 | if (key.empty()) |
7c673cae FG |
1264 | return -EINVAL; |
1265 | ||
11fdf7f2 TL |
1266 | string val; |
1267 | if (conf_stringify(_get_val(values, key), &val) == 0) { | |
224ce89b | 1268 | int l = val.length() + 1; |
7c673cae FG |
1269 | if (len == -1) { |
1270 | *buf = (char*)malloc(l); | |
1271 | if (!*buf) | |
1272 | return -ENOMEM; | |
224ce89b | 1273 | strncpy(*buf, val.c_str(), l); |
7c673cae FG |
1274 | return 0; |
1275 | } | |
224ce89b | 1276 | snprintf(*buf, len, "%s", val.c_str()); |
7c673cae FG |
1277 | return (l > len) ? -ENAMETOOLONG : 0; |
1278 | } | |
1279 | ||
7c673cae FG |
1280 | // couldn't find a configuration option with key 'k' |
1281 | return -ENOENT; | |
1282 | } | |
1283 | ||
1284 | void md_config_t::get_all_keys(std::vector<std::string> *keys) const { | |
1285 | const std::string negative_flag_prefix("no_"); | |
1286 | ||
1287 | keys->clear(); | |
c07f9fc5 FG |
1288 | keys->reserve(schema.size()); |
1289 | for (const auto &i: schema) { | |
1290 | const Option &opt = i.second; | |
7c673cae | 1291 | keys->push_back(opt.name); |
c07f9fc5 | 1292 | if (opt.type == Option::TYPE_BOOL) { |
7c673cae FG |
1293 | keys->push_back(negative_flag_prefix + opt.name); |
1294 | } | |
1295 | } | |
7c673cae FG |
1296 | } |
1297 | ||
1298 | /* The order of the sections here is important. The first section in the | |
1299 | * vector is the "highest priority" section; if we find it there, we'll stop | |
1300 | * looking. The lowest priority section is the one we look in only if all | |
1301 | * others had nothing. This should always be the global section. | |
1302 | */ | |
11fdf7f2 TL |
1303 | void md_config_t::get_my_sections(const ConfigValues& values, |
1304 | std::vector <std::string> §ions) const | |
7c673cae | 1305 | { |
11fdf7f2 | 1306 | _get_my_sections(values, sections); |
7c673cae FG |
1307 | } |
1308 | ||
11fdf7f2 TL |
1309 | void md_config_t::_get_my_sections(const ConfigValues& values, |
1310 | std::vector <std::string> §ions) const | |
7c673cae | 1311 | { |
11fdf7f2 | 1312 | sections.push_back(values.name.to_str()); |
7c673cae | 1313 | |
11fdf7f2 | 1314 | sections.push_back(values.name.get_type_name()); |
7c673cae FG |
1315 | |
1316 | sections.push_back("global"); | |
1317 | } | |
1318 | ||
1319 | // Return a list of all sections | |
1320 | int md_config_t::get_all_sections(std::vector <std::string> §ions) const | |
1321 | { | |
9f95a23c TL |
1322 | for (auto [section_name, section] : cf) { |
1323 | sections.push_back(section_name); | |
1324 | std::ignore = section; | |
7c673cae FG |
1325 | } |
1326 | return 0; | |
1327 | } | |
1328 | ||
11fdf7f2 TL |
1329 | int md_config_t::get_val_from_conf_file( |
1330 | const ConfigValues& values, | |
1331 | const std::vector <std::string> §ions, | |
9f95a23c | 1332 | const std::string_view key, |
11fdf7f2 TL |
1333 | std::string &out, |
1334 | bool emeta) const | |
7c673cae | 1335 | { |
11fdf7f2 TL |
1336 | int r = _get_val_from_conf_file(sections, key, out); |
1337 | if (r < 0) { | |
1338 | return r; | |
1339 | } | |
1340 | if (emeta) { | |
1341 | expand_stack_t stack; | |
1342 | auto v = _expand_meta(values, Option::value_t(out), nullptr, &stack, nullptr); | |
1343 | conf_stringify(v, &out); | |
1344 | } | |
1345 | return 0; | |
7c673cae FG |
1346 | } |
1347 | ||
11fdf7f2 TL |
1348 | int md_config_t::_get_val_from_conf_file( |
1349 | const std::vector <std::string> §ions, | |
9f95a23c | 1350 | const std::string_view key, |
11fdf7f2 | 1351 | std::string &out) const |
7c673cae | 1352 | { |
9f95a23c TL |
1353 | for (auto &s : sections) { |
1354 | int ret = cf.read(s.c_str(), std::string{key}, out); | |
7c673cae | 1355 | if (ret == 0) { |
7c673cae | 1356 | return 0; |
11fdf7f2 | 1357 | } else if (ret != -ENOENT) { |
7c673cae | 1358 | return ret; |
11fdf7f2 | 1359 | } |
7c673cae FG |
1360 | } |
1361 | return -ENOENT; | |
1362 | } | |
1363 | ||
11fdf7f2 TL |
1364 | int md_config_t::_set_val( |
1365 | ConfigValues& values, | |
1366 | const ConfigTracker& observers, | |
1367 | const std::string &raw_val, | |
1368 | const Option &opt, | |
1369 | int level, | |
1370 | std::string *error_message) | |
7c673cae | 1371 | { |
11fdf7f2 | 1372 | Option::value_t new_value; |
81eedcae | 1373 | ceph_assert(error_message); |
11fdf7f2 TL |
1374 | int r = opt.parse_value(raw_val, &new_value, error_message); |
1375 | if (r < 0) { | |
c07f9fc5 FG |
1376 | return r; |
1377 | } | |
7c673cae | 1378 | |
11fdf7f2 TL |
1379 | // unsafe runtime change? |
1380 | if (!opt.can_update_at_runtime() && | |
1381 | safe_to_start_threads && | |
1382 | !observers.is_tracking(opt.name)) { | |
1383 | // accept value if it is not actually a change | |
1384 | if (new_value != _get_val_nometa(values, opt)) { | |
1385 | *error_message = string("Configuration option '") + opt.name + | |
1386 | "' may not be modified at runtime"; | |
9f95a23c | 1387 | return -EPERM; |
c07f9fc5 | 1388 | } |
7c673cae | 1389 | } |
7c673cae | 1390 | |
c07f9fc5 | 1391 | // Apply the value to its entry in the `values` map |
11fdf7f2 TL |
1392 | auto result = values.set_value(opt.name, std::move(new_value), level); |
1393 | switch (result) { | |
1394 | case ConfigValues::SET_NO_CHANGE: | |
1395 | break; | |
1396 | case ConfigValues::SET_NO_EFFECT: | |
1397 | values_bl.clear(); | |
1398 | break; | |
1399 | case ConfigValues::SET_HAVE_EFFECT: | |
1400 | values_bl.clear(); | |
1401 | _refresh(values, opt); | |
1402 | break; | |
1403 | } | |
1404 | return result; | |
1405 | } | |
7c673cae | 1406 | |
11fdf7f2 TL |
1407 | void md_config_t::_refresh(ConfigValues& values, const Option& opt) |
1408 | { | |
c07f9fc5 FG |
1409 | // Apply the value to its legacy field, if it has one |
1410 | auto legacy_ptr_iter = legacy_values.find(std::string(opt.name)); | |
1411 | if (legacy_ptr_iter != legacy_values.end()) { | |
11fdf7f2 TL |
1412 | update_legacy_val(values, opt, legacy_ptr_iter->second); |
1413 | } | |
1414 | // Was this a debug_* option update? | |
1415 | if (opt.subsys >= 0) { | |
1416 | string actual_val; | |
1417 | conf_stringify(_get_val(values, opt), &actual_val); | |
1418 | values.set_logging(opt.subsys, actual_val.c_str()); | |
1419 | } else { | |
1420 | // normal option, advertise the change. | |
1421 | values.changed.insert(opt.name); | |
7c673cae | 1422 | } |
11fdf7f2 | 1423 | } |
7c673cae | 1424 | |
11fdf7f2 | 1425 | int md_config_t::_rm_val(ConfigValues& values, |
9f95a23c | 1426 | const std::string_view key, |
11fdf7f2 TL |
1427 | int level) |
1428 | { | |
1429 | if (schema.count(key) == 0) { | |
1430 | return -EINVAL; | |
1431 | } | |
9f95a23c | 1432 | auto ret = values.rm_val(std::string{key}, level); |
11fdf7f2 TL |
1433 | if (ret < 0) { |
1434 | return ret; | |
1435 | } | |
1436 | if (ret == ConfigValues::SET_HAVE_EFFECT) { | |
1437 | _refresh(values, *find_option(key)); | |
1438 | } | |
1439 | values_bl.clear(); | |
c07f9fc5 FG |
1440 | return 0; |
1441 | } | |
7c673cae | 1442 | |
11fdf7f2 TL |
1443 | namespace { |
1444 | template<typename Size> | |
1445 | struct get_size_visitor : public boost::static_visitor<Size> | |
1446 | { | |
1447 | get_size_visitor() {} | |
1448 | ||
1449 | template<typename T> | |
1450 | Size operator()(const T&) const { | |
1451 | return -1; | |
1452 | } | |
1453 | Size operator()(const Option::size_t& sz) const { | |
1454 | return static_cast<Size>(sz.value); | |
1455 | } | |
1456 | Size operator()(const Size& v) const { | |
1457 | return v; | |
1458 | } | |
1459 | }; | |
1460 | ||
c07f9fc5 FG |
1461 | /** |
1462 | * Handles assigning from a variant-of-types to a variant-of-pointers-to-types | |
1463 | */ | |
11fdf7f2 | 1464 | template<class Config> |
c07f9fc5 FG |
1465 | class assign_visitor : public boost::static_visitor<> |
1466 | { | |
11fdf7f2 | 1467 | Config *conf; |
c07f9fc5 FG |
1468 | Option::value_t val; |
1469 | public: | |
7c673cae | 1470 | |
11fdf7f2 | 1471 | assign_visitor(Config *conf_, Option::value_t val_) |
c07f9fc5 FG |
1472 | : conf(conf_), val(val_) |
1473 | {} | |
1474 | ||
1475 | template <typename T> | |
11fdf7f2 | 1476 | void operator()(T Config::* ptr) const |
c07f9fc5 | 1477 | { |
11fdf7f2 | 1478 | T *member = const_cast<T *>(&(conf->*(boost::get<const T Config::*>(ptr)))); |
c07f9fc5 FG |
1479 | |
1480 | *member = boost::get<T>(val); | |
7c673cae | 1481 | } |
11fdf7f2 TL |
1482 | void operator()(uint64_t Config::* ptr) const |
1483 | { | |
1484 | using T = uint64_t; | |
1485 | auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr)))); | |
1486 | *member = boost::apply_visitor(get_size_visitor<T>{}, val); | |
1487 | } | |
1488 | void operator()(int64_t Config::* ptr) const | |
1489 | { | |
1490 | using T = int64_t; | |
1491 | auto member = const_cast<T*>(&(conf->*(boost::get<const T Config::*>(ptr)))); | |
1492 | *member = boost::apply_visitor(get_size_visitor<T>{}, val); | |
c07f9fc5 | 1493 | } |
7c673cae | 1494 | }; |
11fdf7f2 | 1495 | } // anonymous namespace |
7c673cae | 1496 | |
11fdf7f2 | 1497 | void md_config_t::update_legacy_vals(ConfigValues& values) |
7c673cae | 1498 | { |
11fdf7f2 TL |
1499 | for (const auto &i : legacy_values) { |
1500 | const auto &name = i.first; | |
1501 | const auto &option = schema.at(name); | |
1502 | auto ptr = i.second; | |
1503 | update_legacy_val(values, option, ptr); | |
7c673cae | 1504 | } |
7c673cae FG |
1505 | } |
1506 | ||
11fdf7f2 TL |
1507 | void md_config_t::update_legacy_val(ConfigValues& values, |
1508 | const Option &opt, | |
1509 | md_config_t::member_ptr_t member_ptr) | |
7c673cae | 1510 | { |
11fdf7f2 TL |
1511 | Option::value_t v = _get_val(values, opt); |
1512 | boost::apply_visitor(assign_visitor(&values, v), member_ptr); | |
7c673cae FG |
1513 | } |
1514 | ||
11fdf7f2 | 1515 | static void dump(Formatter *f, int level, Option::value_t in) |
7c673cae | 1516 | { |
11fdf7f2 TL |
1517 | if (const bool *v = boost::get<const bool>(&in)) { |
1518 | f->dump_bool(ceph_conf_level_name(level), *v); | |
1519 | } else if (const int64_t *v = boost::get<const int64_t>(&in)) { | |
1520 | f->dump_int(ceph_conf_level_name(level), *v); | |
1521 | } else if (const uint64_t *v = boost::get<const uint64_t>(&in)) { | |
1522 | f->dump_unsigned(ceph_conf_level_name(level), *v); | |
1523 | } else if (const double *v = boost::get<const double>(&in)) { | |
1524 | f->dump_float(ceph_conf_level_name(level), *v); | |
1525 | } else { | |
1526 | f->dump_stream(ceph_conf_level_name(level)) << Option::to_str(in); | |
7c673cae | 1527 | } |
7c673cae FG |
1528 | } |
1529 | ||
1530 | void md_config_t::diff( | |
11fdf7f2 TL |
1531 | const ConfigValues& values, |
1532 | Formatter *f, | |
1533 | string name) const | |
7c673cae | 1534 | { |
11fdf7f2 | 1535 | values.for_each([this, f, &values] (auto& name, auto& configs) { |
494da23a | 1536 | if (configs.empty()) { |
11fdf7f2 | 1537 | return; |
7c673cae | 1538 | } |
9f95a23c | 1539 | f->open_object_section(std::string{name}.c_str()); |
11fdf7f2 | 1540 | const Option *o = find_option(name); |
494da23a TL |
1541 | if (configs.size() && |
1542 | configs.begin()->first != CONF_DEFAULT) { | |
1543 | // show compiled-in default only if an override default wasn't provided | |
1544 | dump(f, CONF_DEFAULT, _get_val_default(*o)); | |
1545 | } | |
11fdf7f2 TL |
1546 | for (auto& j : configs) { |
1547 | dump(f, j.first, j.second); | |
31f18b77 | 1548 | } |
11fdf7f2 TL |
1549 | dump(f, CONF_FINAL, _get_val(values, *o)); |
1550 | f->close_section(); | |
1551 | }); | |
7c673cae FG |
1552 | } |
1553 | ||
9f95a23c | 1554 | void md_config_t::complain_about_parse_error(CephContext *cct) |
7c673cae | 1555 | { |
9f95a23c | 1556 | ::complain_about_parse_error(cct, parse_error); |
7c673cae | 1557 | } |