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