]> git.proxmox.com Git - ceph.git/blame - ceph/src/mon/ConfigMonitor.cc
bump version to 15.2.11-pve1
[ceph.git] / ceph / src / mon / ConfigMonitor.cc
CommitLineData
11fdf7f2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include <boost/algorithm/string/predicate.hpp>
5
6#include "mon/Monitor.h"
7#include "mon/ConfigMonitor.h"
8#include "mon/MgrMonitor.h"
9#include "mon/OSDMonitor.h"
10#include "messages/MConfig.h"
11#include "messages/MGetConfig.h"
12#include "messages/MMonCommand.h"
13#include "common/Formatter.h"
14#include "common/TextTable.h"
15#include "common/cmdparse.h"
16#include "include/stringify.h"
17
18#define dout_subsys ceph_subsys_mon
19#undef dout_prefix
20#define dout_prefix _prefix(_dout, mon, this)
9f95a23c 21using namespace TOPNSPC::common;
11fdf7f2
TL
22static ostream& _prefix(std::ostream *_dout, const Monitor *mon,
23 const ConfigMonitor *hmon) {
24 return *_dout << "mon." << mon->name << "@" << mon->rank
25 << "(" << mon->get_state_name() << ").config ";
26}
27
28const string KEY_PREFIX("config/");
29const string HISTORY_PREFIX("config-history/");
30
31ConfigMonitor::ConfigMonitor(Monitor *m, Paxos *p, const string& service_name)
32 : PaxosService(m, p, service_name) {
33}
34
35void ConfigMonitor::init()
36{
37 dout(10) << __func__ << dendl;
38}
39
40void ConfigMonitor::create_initial()
41{
42 dout(10) << __func__ << dendl;
43 version = 0;
44 pending.clear();
45}
46
47void ConfigMonitor::update_from_paxos(bool *need_bootstrap)
48{
49 if (version == get_last_committed()) {
50 return;
51 }
52 version = get_last_committed();
53 dout(10) << __func__ << " " << version << dendl;
54 load_config();
55 check_all_subs();
56}
57
58void ConfigMonitor::create_pending()
59{
60 dout(10) << " " << version << dendl;
61 pending.clear();
62 pending_description.clear();
63}
64
65void ConfigMonitor::encode_pending(MonitorDBStore::TransactionRef t)
66{
67 dout(10) << " " << (version+1) << dendl;
68 put_last_committed(t, version+1);
69
92f5a8d4
TL
70 for (auto& [key, value] : pending_cleanup) {
71 if (pending.count(key) == 0) {
72 derr << __func__ << " repair: adjusting config key '" << key << "'"
73 << dendl;
74 pending[key] = value;
75 }
76 }
77 pending_cleanup.clear();
78
11fdf7f2
TL
79 // TODO: record changed sections (osd, mds.foo, rack:bar, ...)
80
81 string history = HISTORY_PREFIX + stringify(version+1) + "/";
82 {
83 bufferlist metabl;
84 ::encode(ceph_clock_now(), metabl);
85 ::encode(pending_description, metabl);
86 t->put(CONFIG_PREFIX, history, metabl);
87 }
88 for (auto& p : pending) {
89 string key = KEY_PREFIX + p.first;
90 auto q = current.find(p.first);
91 if (q != current.end()) {
92 if (p.second && *p.second == q->second) {
93 continue;
94 }
95 t->put(CONFIG_PREFIX, history + "-" + p.first, q->second);
96 } else if (!p.second) {
97 continue;
98 }
99 if (p.second) {
100 dout(20) << __func__ << " set " << key << dendl;
101 t->put(CONFIG_PREFIX, key, *p.second);
102 t->put(CONFIG_PREFIX, history + "+" + p.first, *p.second);
103 } else {
104 dout(20) << __func__ << " rm " << key << dendl;
105 t->erase(CONFIG_PREFIX, key);
106 }
107 }
108}
109
110version_t ConfigMonitor::get_trim_to() const
111{
112 // we don't actually need *any* old states, but keep a few.
113 if (version > 5) {
114 return version - 5;
115 }
116 return 0;
117}
118
119bool ConfigMonitor::preprocess_query(MonOpRequestRef op)
120{
121 switch (op->get_req()->get_type()) {
122 case MSG_MON_COMMAND:
123 try {
124 return preprocess_command(op);
125 } catch (const bad_cmd_get& e) {
126 bufferlist bl;
127 mon->reply_command(op, -EINVAL, e.what(), bl, get_last_committed());
128 return true;
129 }
130 }
131 return false;
132}
133
134static string indent_who(const string& who)
135{
136 if (who == "global") {
137 return who;
138 }
139 if (who.find('.') == string::npos) {
140 return " " + who;
141 }
142 return " " + who;
143}
144
145bool ConfigMonitor::preprocess_command(MonOpRequestRef op)
146{
9f95a23c 147 auto m = op->get_req<MMonCommand>();
11fdf7f2
TL
148 std::stringstream ss;
149 int err = 0;
150
151 cmdmap_t cmdmap;
152 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
153 string rs = ss.str();
154 mon->reply_command(op, -EINVAL, rs, get_last_committed());
155 return true;
156 }
157 string format;
9f95a23c 158 cmd_getval(cmdmap, "format", format, string("plain"));
11fdf7f2
TL
159 boost::scoped_ptr<Formatter> f(Formatter::create(format));
160
161 string prefix;
9f95a23c 162 cmd_getval(cmdmap, "prefix", prefix);
11fdf7f2
TL
163
164 bufferlist odata;
165 if (prefix == "config help") {
166 stringstream ss;
167 string name;
9f95a23c 168 cmd_getval(cmdmap, "key", name);
11fdf7f2
TL
169 const Option *opt = g_conf().find_option(name);
170 if (!opt) {
171 opt = mon->mgrmon()->find_module_option(name);
172 }
173 if (opt) {
174 if (f) {
175 f->dump_object("option", *opt);
176 } else {
177 opt->print(&ss);
178 }
179 } else {
180 ss << "configuration option '" << name << "' not recognized";
181 err = -ENOENT;
182 goto reply;
183 }
184 if (f) {
185 f->flush(odata);
186 } else {
187 odata.append(ss.str());
188 }
189 } else if (prefix == "config ls") {
190 ostringstream ss;
191 if (f) {
192 f->open_array_section("options");
193 }
194 for (auto& i : ceph_options) {
195 if (f) {
196 f->dump_string("option", i.name);
197 } else {
198 ss << i.name << "\n";
199 }
200 }
201 for (auto& i : mon->mgrmon()->get_mgr_module_options()) {
202 if (f) {
203 f->dump_string("option", i.first);
204 } else {
205 ss << i.first << "\n";
206 }
207 }
208 if (f) {
209 f->close_section();
210 f->flush(odata);
211 } else {
212 odata.append(ss.str());
213 }
214 } else if (prefix == "config dump") {
215 list<pair<string,Section*>> sections = {
216 make_pair("global", &config_map.global)
217 };
218 for (string type : { "mon", "mgr", "osd", "mds", "client" }) {
219 auto i = config_map.by_type.find(type);
220 if (i != config_map.by_type.end()) {
221 sections.push_back(make_pair(i->first, &i->second));
222 }
223 auto j = config_map.by_id.lower_bound(type);
224 while (j != config_map.by_id.end() &&
225 j->first.find(type) == 0) {
226 sections.push_back(make_pair(j->first, &j->second));
227 ++j;
228 }
229 }
230 TextTable tbl;
231 if (!f) {
232 tbl.define_column("WHO", TextTable::LEFT, TextTable::LEFT);
233 tbl.define_column("MASK", TextTable::LEFT, TextTable::LEFT);
234 tbl.define_column("LEVEL", TextTable::LEFT, TextTable::LEFT);
235 tbl.define_column("OPTION", TextTable::LEFT, TextTable::LEFT);
236 tbl.define_column("VALUE", TextTable::LEFT, TextTable::LEFT);
237 tbl.define_column("RO", TextTable::LEFT, TextTable::LEFT);
238 } else {
239 f->open_array_section("config");
240 }
241 for (auto s : sections) {
242 for (auto& i : s.second->options) {
243 if (!f) {
244 tbl << indent_who(s.first);
245 tbl << i.second.mask.to_str();
246 tbl << Option::level_to_str(i.second.opt->level);
247 tbl << i.first;
248 tbl << i.second.raw_value;
249 tbl << (i.second.opt->can_update_at_runtime() ? "" : "*");
250 tbl << TextTable::endrow;
251 } else {
252 f->open_object_section("option");
253 f->dump_string("section", s.first);
254 i.second.dump(f.get());
255 f->close_section();
256 }
257 }
258 }
259 if (!f) {
260 odata.append(stringify(tbl));
261 } else {
262 f->close_section();
263 f->flush(odata);
264 }
265 } else if (prefix == "config get") {
266 string who, name;
9f95a23c 267 cmd_getval(cmdmap, "who", who);
11fdf7f2
TL
268
269 EntityName entity;
9f95a23c
TL
270 if (!entity.from_str(who) &&
271 !entity.from_str(who + ".")) {
11fdf7f2
TL
272 ss << "unrecognized entity '" << who << "'";
273 err = -EINVAL;
274 goto reply;
275 }
276
277 map<string,string> crush_location;
278 string device_class;
279 if (entity.is_osd()) {
280 mon->osdmon()->osdmap.crush->get_full_location(who, &crush_location);
281 int id = atoi(entity.get_id().c_str());
282 const char *c = mon->osdmon()->osdmap.crush->get_item_class(id);
283 if (c) {
284 device_class = c;
285 }
286 dout(10) << __func__ << " crush_location " << crush_location
287 << " class " << device_class << dendl;
288 }
289
11fdf7f2 290 std::map<std::string,pair<std::string,const MaskedOption*>> src;
9f95a23c 291 auto config = config_map.generate_entity_map(
11fdf7f2
TL
292 entity,
293 crush_location,
294 mon->osdmon()->osdmap.crush.get(),
295 device_class,
9f95a23c 296 &src);
11fdf7f2 297
9f95a23c 298 if (cmd_getval(cmdmap, "key", name)) {
11fdf7f2
TL
299 const Option *opt = g_conf().find_option(name);
300 if (!opt) {
301 opt = mon->mgrmon()->find_module_option(name);
302 }
303 if (!opt) {
304 err = -ENOENT;
305 goto reply;
306 }
92f5a8d4
TL
307 if (opt->has_flag(Option::FLAG_NO_MON_UPDATE)) {
308 // handle special options
309 if (name == "fsid") {
310 odata.append(stringify(mon->monmap->get_fsid()));
311 odata.append("\n");
312 goto reply;
313 }
314 err = -EINVAL;
315 ss << name << " is special and cannot be stored by the mon";
316 goto reply;
317 }
318 // get a single value
319 auto p = config.find(name);
320 if (p != config.end()) {
321 odata.append(p->second);
322 odata.append("\n");
323 goto reply;
324 }
11fdf7f2
TL
325 if (!entity.is_client() &&
326 !boost::get<boost::blank>(&opt->daemon_value)) {
327 odata.append(Option::to_str(opt->daemon_value));
328 } else {
329 odata.append(Option::to_str(opt->value));
330 }
331 odata.append("\n");
332 } else {
333 // dump all (non-default) values for this entity
334 TextTable tbl;
335 if (!f) {
336 tbl.define_column("WHO", TextTable::LEFT, TextTable::LEFT);
337 tbl.define_column("MASK", TextTable::LEFT, TextTable::LEFT);
338 tbl.define_column("LEVEL", TextTable::LEFT, TextTable::LEFT);
339 tbl.define_column("OPTION", TextTable::LEFT, TextTable::LEFT);
340 tbl.define_column("VALUE", TextTable::LEFT, TextTable::LEFT);
341 tbl.define_column("RO", TextTable::LEFT, TextTable::LEFT);
342 } else {
343 f->open_object_section("config");
344 }
345 auto p = config.begin();
346 auto q = src.begin();
347 for (; p != config.end(); ++p, ++q) {
348 if (name.size() && p->first != name) {
349 continue;
350 }
351 if (!f) {
352 tbl << q->second.first;
353 tbl << q->second.second->mask.to_str();
354 tbl << Option::level_to_str(q->second.second->opt->level);
355 tbl << p->first;
356 tbl << p->second;
357 tbl << (q->second.second->opt->can_update_at_runtime() ? "" : "*");
358 tbl << TextTable::endrow;
359 } else {
360 f->open_object_section(p->first.c_str());
361 f->dump_string("value", p->second);
362 f->dump_string("section", q->second.first);
363 f->dump_object("mask", q->second.second->mask);
364 f->dump_bool("can_update_at_runtime",
365 q->second.second->opt->can_update_at_runtime());
366 f->close_section();
367 }
368 }
369 if (!f) {
370 odata.append(stringify(tbl));
371 } else {
372 f->close_section();
373 f->flush(odata);
374 }
375 }
376 } else if (prefix == "config log") {
377 int64_t num = 10;
9f95a23c 378 cmd_getval(cmdmap, "num", num);
11fdf7f2
TL
379 ostringstream ds;
380 if (f) {
381 f->open_array_section("changesets");
382 }
383 for (version_t v = version; v > version - std::min(version, (version_t)num); --v) {
384 ConfigChangeSet ch;
385 load_changeset(v, &ch);
386 if (f) {
387 f->dump_object("changeset", ch);
388 } else {
389 ch.print(ds);
390 }
391 }
392 if (f) {
393 f->close_section();
394 f->flush(odata);
395 } else {
396 odata.append(ds.str());
397 }
398 } else if (prefix == "config generate-minimal-conf") {
399 ostringstream conf;
400 conf << "# minimal ceph.conf for " << mon->monmap->get_fsid() << "\n";
401
402 // the basics
403 conf << "[global]\n";
404 conf << "\tfsid = " << mon->monmap->get_fsid() << "\n";
405 conf << "\tmon_host = ";
406 for (auto i = mon->monmap->mon_info.begin();
407 i != mon->monmap->mon_info.end();
408 ++i) {
409 if (i != mon->monmap->mon_info.begin()) {
410 conf << " ";
411 }
9f95a23c
TL
412 if (i->second.public_addrs.size() == 1 &&
413 i->second.public_addrs.front().is_legacy() &&
414 i->second.public_addrs.front().get_port() == CEPH_MON_PORT_LEGACY) {
415 // if this is a legacy addr on the legacy default port, then
416 // use the legacy-compatible formatting so that old clients
417 // can use this config. new code will see the :6789 and correctly
418 // interpret this as a v1 address.
419 conf << i->second.public_addrs.get_legacy_str();
420 } else {
421 conf << i->second.public_addrs;
422 }
11fdf7f2
TL
423 }
424 conf << "\n";
425 conf << config_map.global.get_minimal_conf();
426 for (auto m : { &config_map.by_type, &config_map.by_id }) {
427 for (auto& i : *m) {
428 auto s = i.second.get_minimal_conf();
429 if (s.size()) {
430 conf << "\n[" << i.first << "]\n" << s;
431 }
432 }
433 }
434 odata.append(conf.str());
435 err = 0;
436 } else {
437 return false;
438 }
439
440 reply:
441 mon->reply_command(op, err, ss.str(), odata, get_last_committed());
442 return true;
443}
444
445void ConfigMonitor::handle_get_config(MonOpRequestRef op)
446{
9f95a23c 447 auto m = op->get_req<MGetConfig>();
11fdf7f2
TL
448 dout(10) << __func__ << " " << m->name << " host " << m->host << dendl;
449
450 const OSDMap& osdmap = mon->osdmon()->osdmap;
451 map<string,string> crush_location;
452 osdmap.crush->get_full_location(m->host, &crush_location);
9f95a23c 453 auto out = config_map.generate_entity_map(
11fdf7f2
TL
454 m->name,
455 crush_location,
456 osdmap.crush.get(),
9f95a23c 457 m->device_class);
11fdf7f2 458 dout(20) << " config is " << out << dendl;
9f95a23c 459 m->get_connection()->send_message(new MConfig{std::move(out)});
11fdf7f2
TL
460}
461
462bool ConfigMonitor::prepare_update(MonOpRequestRef op)
463{
464 Message *m = op->get_req();
465 dout(7) << "prepare_update " << *m
466 << " from " << m->get_orig_source_inst() << dendl;
467 switch (m->get_type()) {
468 case MSG_MON_COMMAND:
469 try {
470 return prepare_command(op);
471 } catch (const bad_cmd_get& e) {
472 bufferlist bl;
473 mon->reply_command(op, -EINVAL, e.what(), bl, get_last_committed());
474 return true;
475 }
476 }
477 return false;
478}
479
480bool ConfigMonitor::prepare_command(MonOpRequestRef op)
481{
9f95a23c 482 auto m = op->get_req<MMonCommand>();
11fdf7f2
TL
483 std::stringstream ss;
484 int err = -EINVAL;
485
486 cmdmap_t cmdmap;
487 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
488 string rs = ss.str();
489 mon->reply_command(op, -EINVAL, rs, get_last_committed());
490 return true;
491 }
492
493 string prefix;
9f95a23c 494 cmd_getval(cmdmap, "prefix", prefix);
11fdf7f2
TL
495 bufferlist odata;
496
497 if (prefix == "config set" ||
498 prefix == "config rm") {
499 string who;
500 string name, value;
501 bool force = false;
9f95a23c
TL
502 cmd_getval(cmdmap, "who", who);
503 cmd_getval(cmdmap, "name", name);
504 cmd_getval(cmdmap, "value", value);
505 cmd_getval(cmdmap, "force", force);
11fdf7f2
TL
506
507 if (prefix == "config set" && !force) {
508 const Option *opt = g_conf().find_option(name);
509 if (!opt) {
510 opt = mon->mgrmon()->find_module_option(name);
511 }
512 if (!opt) {
513 ss << "unrecognized config option '" << name << "'";
514 err = -EINVAL;
515 goto reply;
516 }
517
92f5a8d4
TL
518 Option::value_t real_value;
519 string errstr;
520 err = opt->parse_value(value, &real_value, &errstr, &value);
521 if (err < 0) {
522 ss << "error parsing value: " << errstr;
523 goto reply;
524 }
525
526 if (opt->has_flag(Option::FLAG_NO_MON_UPDATE)) {
527 err = -EINVAL;
528 ss << name << " is special and cannot be stored by the mon";
529 goto reply;
11fdf7f2
TL
530 }
531 }
532
533 string section;
534 OptionMask mask;
535 if (!ConfigMap::parse_mask(who, &section, &mask)) {
536 ss << "unrecognized config target '" << who << "'";
537 err = -EINVAL;
538 goto reply;
539 }
540
541 string key;
542 if (section.size()) {
543 key += section + "/";
92f5a8d4
TL
544 } else {
545 key += "global/";
11fdf7f2
TL
546 }
547 string mask_str = mask.to_str();
548 if (mask_str.size()) {
549 key += mask_str + "/";
550 }
551 key += name;
552
553 if (prefix == "config set") {
554 bufferlist bl;
555 bl.append(value);
556 pending[key] = bl;
557 } else {
558 pending[key] = boost::none;
559 }
560 goto update;
561 } else if (prefix == "config reset") {
9f95a23c
TL
562 int64_t revert_to = -1;
563 cmd_getval(cmdmap, "num", revert_to);
564 if (revert_to < 0 ||
565 revert_to > (int64_t)version) {
11fdf7f2 566 err = -EINVAL;
9f95a23c
TL
567 ss << "must specify a valid historical version to revert to; "
568 << "see 'ceph config log' for a list of avialable configuration "
569 << "historical versions";
11fdf7f2
TL
570 goto reply;
571 }
9f95a23c 572 if (revert_to == (int64_t)version) {
11fdf7f2
TL
573 err = 0;
574 goto reply;
575 }
9f95a23c 576 for (int64_t v = version; v > revert_to; --v) {
11fdf7f2
TL
577 ConfigChangeSet ch;
578 load_changeset(v, &ch);
579 for (auto& i : ch.diff) {
580 if (i.second.first) {
581 bufferlist bl;
582 bl.append(*i.second.first);
583 pending[i.first] = bl;
584 } else if (i.second.second) {
585 pending[i.first] = boost::none;
586 }
587 }
588 }
9f95a23c 589 pending_description = string("reset to ") + stringify(revert_to);
11fdf7f2
TL
590 goto update;
591 } else if (prefix == "config assimilate-conf") {
592 ConfFile cf;
11fdf7f2 593 bufferlist bl = m->get_data();
9f95a23c 594 err = cf.parse_bufferlist(&bl, &ss);
11fdf7f2 595 if (err < 0) {
11fdf7f2
TL
596 goto reply;
597 }
598 bool updated = false;
599 ostringstream newconf;
9f95a23c 600 for (auto& [section, s] : cf) {
11fdf7f2
TL
601 dout(20) << __func__ << " [" << section << "]" << dendl;
602 bool did_section = false;
9f95a23c 603 for (auto& [key, val] : s) {
11fdf7f2
TL
604 Option::value_t real_value;
605 string value;
606 string errstr;
9f95a23c 607 if (key.empty()) {
11fdf7f2
TL
608 continue;
609 }
610 // a known and worthy option?
9f95a23c 611 const Option *o = g_conf().find_option(key);
11fdf7f2 612 if (!o) {
9f95a23c 613 o = mon->mgrmon()->find_module_option(key);
11fdf7f2
TL
614 }
615 if (!o ||
9f95a23c
TL
616 (o->flags & Option::FLAG_NO_MON_UPDATE) ||
617 (o->flags & Option::FLAG_CLUSTER_CREATE)) {
11fdf7f2
TL
618 goto skip;
619 }
620 // normalize
9f95a23c 621 err = o->parse_value(val, &real_value, &errstr, &value);
11fdf7f2 622 if (err < 0) {
9f95a23c
TL
623 dout(20) << __func__ << " failed to parse " << key << " = '"
624 << val << "'" << dendl;
11fdf7f2
TL
625 goto skip;
626 }
627 // does it conflict with an existing value?
628 {
629 const Section *s = config_map.find_section(section);
630 if (s) {
9f95a23c 631 auto k = s->options.find(key);
11fdf7f2
TL
632 if (k != s->options.end()) {
633 if (value != k->second.raw_value) {
9f95a23c 634 dout(20) << __func__ << " have " << key
11fdf7f2
TL
635 << " = " << k->second.raw_value
636 << " (not " << value << ")" << dendl;
637 goto skip;
638 }
9f95a23c 639 dout(20) << __func__ << " already have " << key
11fdf7f2
TL
640 << " = " << k->second.raw_value << dendl;
641 continue;
642 }
643 }
644 }
9f95a23c
TL
645 dout(20) << __func__ << " add " << key << " = " << value
646 << " (" << val << ")" << dendl;
11fdf7f2 647 {
11fdf7f2
TL
648 bufferlist bl;
649 bl.append(value);
9f95a23c 650 pending[section + "/" + key] = bl;
11fdf7f2
TL
651 updated = true;
652 }
653 continue;
654
655 skip:
9f95a23c
TL
656 dout(20) << __func__ << " skip " << key << " = " << value
657 << " (" << val << ")" << dendl;
11fdf7f2
TL
658 if (!did_section) {
659 newconf << "\n[" << section << "]\n";
660 did_section = true;
661 }
9f95a23c 662 newconf << "\t" << key << " = " << val << "\n";
11fdf7f2
TL
663 }
664 }
665 odata.append(newconf.str());
666 if (updated) {
667 goto update;
668 }
669 } else {
670 ss << "unknown command " << prefix;
671 err = -EINVAL;
672 }
673
674reply:
675 mon->reply_command(op, err, ss.str(), odata, get_last_committed());
676 return false;
677
678update:
679 // see if there is an actual change
680 auto p = pending.begin();
681 while (p != pending.end()) {
682 auto q = current.find(p->first);
683 if (p->second && q != current.end() && *p->second == q->second) {
684 // set to same value
685 p = pending.erase(p);
686 } else if (!p->second && q == current.end()) {
687 // erasing non-existent value
688 p = pending.erase(p);
689 } else {
690 ++p;
691 }
692 }
693 if (pending.empty()) {
694 err = 0;
695 goto reply;
696 }
697 force_immediate_propose(); // faster response
698 wait_for_finished_proposal(
699 op,
700 new Monitor::C_Command(
701 mon, op, 0, ss.str(), odata,
702 get_last_committed() + 1));
703 return true;
704}
705
706void ConfigMonitor::tick()
707{
92f5a8d4 708 if (!is_active() || !mon->is_leader()) {
11fdf7f2
TL
709 return;
710 }
711 dout(10) << __func__ << dendl;
712 bool changed = false;
92f5a8d4
TL
713 if (!pending_cleanup.empty()) {
714 changed = true;
715 }
11fdf7f2
TL
716 if (changed) {
717 propose_pending();
718 }
719}
720
721void ConfigMonitor::on_active()
722{
723}
724
725void ConfigMonitor::load_config()
726{
727 unsigned num = 0;
728 KeyValueDB::Iterator it = mon->store->get_iterator(CONFIG_PREFIX);
729 it->lower_bound(KEY_PREFIX);
730 config_map.clear();
731 current.clear();
92f5a8d4 732 pending_cleanup.clear();
11fdf7f2
TL
733 while (it->valid() &&
734 it->key().compare(0, KEY_PREFIX.size(), KEY_PREFIX) == 0) {
735 string key = it->key().substr(KEY_PREFIX.size());
736 string value = it->value().to_str();
737
738 current[key] = it->value();
739
740 auto last_slash = key.rfind('/');
741 string name;
742 string who;
743 if (last_slash == std::string::npos) {
744 name = key;
745 } else if (auto mgrpos = key.find("/mgr/"); mgrpos != std::string::npos) {
746 name = key.substr(mgrpos + 1);
747 who = key.substr(0, mgrpos);
748 } else {
749 name = key.substr(last_slash + 1);
750 who = key.substr(0, last_slash);
751 }
752
753 const Option *opt = g_conf().find_option(name);
754 if (!opt) {
755 opt = mon->mgrmon()->find_module_option(name);
756 }
757 if (!opt) {
758 dout(10) << __func__ << " unrecognized option '" << name << "'" << dendl;
759 opt = new Option(name, Option::TYPE_STR, Option::LEVEL_UNKNOWN);
760 // FIXME: this will be leaked!
761 }
762
763 string err;
764 int r = opt->pre_validate(&value, &err);
765 if (r < 0) {
766 dout(10) << __func__ << " pre-validate failed on '" << name << "' = '"
767 << value << "' for " << name << dendl;
768 }
769
770 MaskedOption mopt(opt);
771 mopt.raw_value = value;
772 string section_name;
773 if (who.size() &&
774 !ConfigMap::parse_mask(who, &section_name, &mopt.mask)) {
92f5a8d4
TL
775 derr << __func__ << " invalid mask for key " << key << dendl;
776 pending_cleanup[key] = boost::none;
777 } else if (opt->has_flag(Option::FLAG_NO_MON_UPDATE)) {
778 dout(10) << __func__ << " NO_MON_UPDATE option '"
779 << name << "' = '" << value << "' for " << name
780 << dendl;
781 pending_cleanup[key] = boost::none;
11fdf7f2 782 } else {
92f5a8d4
TL
783 if (section_name.empty()) {
784 // we prefer global/$option instead of just $option
785 derr << __func__ << " adding global/ prefix to key '" << key << "'"
786 << dendl;
787 pending_cleanup[key] = boost::none;
788 pending_cleanup["global/"s + key] = it->value();
789 }
11fdf7f2 790 Section *section = &config_map.global;;
92f5a8d4 791 if (section_name.size() && section_name != "global") {
11fdf7f2
TL
792 if (section_name.find('.') != std::string::npos) {
793 section = &config_map.by_id[section_name];
794 } else {
795 section = &config_map.by_type[section_name];
796 }
797 }
798 section->options.insert(make_pair(name, std::move(mopt)));
799 ++num;
800 }
801 it->next();
802 }
803 dout(10) << __func__ << " got " << num << " keys" << dendl;
11fdf7f2
TL
804
805 // refresh our own config
806 {
807 const OSDMap& osdmap = mon->osdmon()->osdmap;
808 map<string,string> crush_location;
809 osdmap.crush->get_full_location(g_conf()->host, &crush_location);
9f95a23c 810 auto out = config_map.generate_entity_map(
11fdf7f2
TL
811 g_conf()->name,
812 crush_location,
813 osdmap.crush.get(),
9f95a23c 814 string{}); // no device class
11fdf7f2
TL
815 g_conf().set_mon_vals(g_ceph_context, out, nullptr);
816 }
817}
818
819void ConfigMonitor::load_changeset(version_t v, ConfigChangeSet *ch)
820{
821 ch->version = v;
822 string prefix = HISTORY_PREFIX + stringify(v) + "/";
823 KeyValueDB::Iterator it = mon->store->get_iterator(CONFIG_PREFIX);
824 it->lower_bound(prefix);
825 while (it->valid() && it->key().find(prefix) == 0) {
826 if (it->key() == prefix) {
827 bufferlist bl = it->value();
828 auto p = bl.cbegin();
829 try {
830 decode(ch->stamp, p);
831 decode(ch->name, p);
832 }
833 catch (buffer::error& e) {
834 derr << __func__ << " failure decoding changeset " << v << dendl;
835 }
836 } else {
837 char op = it->key()[prefix.length()];
838 string key = it->key().substr(prefix.length() + 1);
839 if (op == '-') {
840 ch->diff[key].first = it->value().to_str();
841 } else if (op == '+') {
842 ch->diff[key].second = it->value().to_str();
843 }
844 }
845 it->next();
846 }
847}
848
849bool ConfigMonitor::refresh_config(MonSession *s)
850{
851 const OSDMap& osdmap = mon->osdmon()->osdmap;
852 map<string,string> crush_location;
853 if (s->remote_host.size()) {
854 osdmap.crush->get_full_location(s->remote_host, &crush_location);
855 dout(10) << __func__ << " crush_location for remote_host " << s->remote_host
856 << " is " << crush_location << dendl;
857 }
858
859 string device_class;
860 if (s->name.is_osd()) {
861 const char *c = osdmap.crush->get_item_class(s->name.num());
862 if (c) {
863 device_class = c;
864 dout(10) << __func__ << " device_class " << device_class << dendl;
865 }
866 }
867
868 dout(20) << __func__ << " " << s->entity_name << " crush " << crush_location
869 << " device_class " << device_class << dendl;
9f95a23c 870 auto out = config_map.generate_entity_map(
11fdf7f2
TL
871 s->entity_name,
872 crush_location,
873 osdmap.crush.get(),
9f95a23c 874 device_class);
11fdf7f2
TL
875
876 if (out == s->last_config && s->any_config) {
877 dout(20) << __func__ << " no change, " << out << dendl;
878 return false;
879 }
adb31ebb
TL
880 // removing this to hide sensitive data going into logs
881 // leaving this for debugging purposes
882 // dout(20) << __func__ << " " << out << dendl;
9f95a23c 883 s->last_config = std::move(out);
11fdf7f2
TL
884 s->any_config = true;
885 return true;
886}
887
888bool ConfigMonitor::maybe_send_config(MonSession *s)
889{
890 bool changed = refresh_config(s);
891 dout(10) << __func__ << " to " << s->name << " "
892 << (changed ? "(changed)" : "(unchanged)")
893 << dendl;
894 if (changed) {
895 send_config(s);
896 }
897 return changed;
898}
899
900void ConfigMonitor::send_config(MonSession *s)
901{
902 dout(10) << __func__ << " to " << s->name << dendl;
903 auto m = new MConfig(s->last_config);
904 s->con->send_message(m);
905}
906
907void ConfigMonitor::check_sub(MonSession *s)
908{
909 if (!s->authenticated) {
910 dout(20) << __func__ << " not authenticated " << s->entity_name << dendl;
911 return;
912 }
913 auto p = s->sub_map.find("config");
914 if (p != s->sub_map.end()) {
915 check_sub(p->second);
916 }
917}
918
919void ConfigMonitor::check_sub(Subscription *sub)
920{
921 dout(10) << __func__
922 << " next " << sub->next
923 << " have " << version << dendl;
924 if (sub->next <= version) {
925 maybe_send_config(sub->session);
926 if (sub->onetime) {
927 mon->with_session_map([sub](MonSessionMap& session_map) {
928 session_map.remove_sub(sub);
929 });
930 } else {
931 sub->next = version + 1;
932 }
933 }
934}
935
936void ConfigMonitor::check_all_subs()
937{
938 dout(10) << __func__ << dendl;
939 auto subs = mon->session_map.subs.find("config");
940 if (subs == mon->session_map.subs.end()) {
941 return;
942 }
943 int updated = 0, total = 0;
944 auto p = subs->second->begin();
945 while (!p.end()) {
946 auto sub = *p;
947 ++p;
948 ++total;
949 if (maybe_send_config(sub->session)) {
950 ++updated;
951 }
952 }
953 dout(10) << __func__ << " updated " << updated << " / " << total << dendl;
954}