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