]>
git.proxmox.com Git - ceph.git/blob - ceph/src/mon/ConfigMonitor.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include <boost/algorithm/string/predicate.hpp>
6 #include "mon/Monitor.h"
7 #include "mon/ConfigMonitor.h"
8 #include "mon/KVMonitor.h"
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"
19 #define dout_subsys ceph_subsys_mon
21 #define dout_prefix _prefix(_dout, mon, this)
22 using namespace TOPNSPC::common
;
24 using namespace std::literals
;
34 using std::ostringstream
;
39 using std::stringstream
;
42 using std::unique_ptr
;
44 using ceph::bufferlist
;
47 using ceph::Formatter
;
48 using ceph::JSONFormatter
;
49 using ceph::mono_clock
;
50 using ceph::mono_time
;
51 using ceph::timespan_str
;
52 static ostream
& _prefix(std::ostream
*_dout
, const Monitor
&mon
,
53 const ConfigMonitor
*hmon
) {
54 return *_dout
<< "mon." << mon
.name
<< "@" << mon
.rank
55 << "(" << mon
.get_state_name() << ").config ";
58 const string
KEY_PREFIX("config/");
59 const string
HISTORY_PREFIX("config-history/");
61 ConfigMonitor::ConfigMonitor(Monitor
&m
, Paxos
&p
, const string
& service_name
)
62 : PaxosService(m
, p
, service_name
) {
65 void ConfigMonitor::init()
67 dout(10) << __func__
<< dendl
;
70 void ConfigMonitor::create_initial()
72 dout(10) << __func__
<< dendl
;
77 void ConfigMonitor::update_from_paxos(bool *need_bootstrap
)
79 if (version
== get_last_committed()) {
82 version
= get_last_committed();
83 dout(10) << __func__
<< " " << version
<< dendl
;
88 void ConfigMonitor::create_pending()
90 dout(10) << " " << version
<< dendl
;
92 pending_description
.clear();
95 void ConfigMonitor::encode_pending(MonitorDBStore::TransactionRef t
)
97 dout(10) << " " << (version
+1) << dendl
;
98 put_last_committed(t
, version
+1);
99 // NOTE: caller should have done encode_pending_to_kvmon() and
100 // kvmon->propose_pending() to commit the actual config changes.
103 void ConfigMonitor::encode_pending_to_kvmon()
105 // we need to pass our data through KVMonitor so that it is properly
106 // versioned and shared with subscribers.
107 for (auto& [key
, value
] : pending_cleanup
) {
108 if (pending
.count(key
) == 0) {
109 derr
<< __func__
<< " repair: adjusting config key '" << key
<< "'"
111 pending
[key
] = value
;
114 pending_cleanup
.clear();
116 // TODO: record changed sections (osd, mds.foo, rack:bar, ...)
118 string history
= HISTORY_PREFIX
+ stringify(version
+1) + "/";
121 ::encode(ceph_clock_now(), metabl
);
122 ::encode(pending_description
, metabl
);
123 mon
.kvmon()->enqueue_set(history
, metabl
);
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
) {
132 mon
.kvmon()->enqueue_set(history
+ "-" + p
.first
, q
->second
);
133 } else if (!p
.second
) {
137 dout(20) << __func__
<< " set " << key
<< dendl
;
138 mon
.kvmon()->enqueue_set(key
, *p
.second
);
139 mon
.kvmon()->enqueue_set(history
+ "+" + p
.first
, *p
.second
);
141 dout(20) << __func__
<< " rm " << key
<< dendl
;
142 mon
.kvmon()->enqueue_rm(key
);
147 version_t
ConfigMonitor::get_trim_to() const
149 // we don't actually need *any* old states, but keep a few.
156 bool ConfigMonitor::preprocess_query(MonOpRequestRef op
)
158 switch (op
->get_req()->get_type()) {
159 case MSG_MON_COMMAND
:
161 return preprocess_command(op
);
162 } catch (const bad_cmd_get
& e
) {
164 mon
.reply_command(op
, -EINVAL
, e
.what(), bl
, get_last_committed());
171 bool ConfigMonitor::preprocess_command(MonOpRequestRef op
)
173 auto m
= op
->get_req
<MMonCommand
>();
174 std::stringstream ss
;
178 if (!cmdmap_from_json(m
->cmd
, &cmdmap
, ss
)) {
179 string rs
= ss
.str();
180 mon
.reply_command(op
, -EINVAL
, rs
, get_last_committed());
183 string format
= cmd_getval_or
<string
>(cmdmap
, "format", "plain");
184 boost::scoped_ptr
<Formatter
> f(Formatter::create(format
));
187 cmd_getval(cmdmap
, "prefix", prefix
);
190 if (prefix
== "config help") {
193 cmd_getval(cmdmap
, "key", name
);
194 const Option
*opt
= g_conf().find_option(name
);
196 opt
= mon
.mgrmon()->find_module_option(name
);
200 f
->dump_object("option", *opt
);
205 ss
<< "configuration option '" << name
<< "' not recognized";
212 odata
.append(ss
.str());
214 } else if (prefix
== "config ls") {
217 f
->open_array_section("options");
219 for (auto& i
: ceph_options
) {
221 f
->dump_string("option", i
.name
);
223 ss
<< i
.name
<< "\n";
226 for (auto& i
: mon
.mgrmon()->get_mgr_module_options()) {
228 f
->dump_string("option", i
.first
);
230 ss
<< i
.first
<< "\n";
237 odata
.append(ss
.str());
239 } else if (prefix
== "config dump") {
240 list
<pair
<string
,Section
*>> sections
= {
241 make_pair("global", &config_map
.global
)
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
));
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
));
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
);
264 f
->open_array_section("config");
266 for (auto s
: sections
) {
267 for (auto& i
: s
.second
->options
) {
270 tbl
<< i
.second
.mask
.to_str();
271 tbl
<< Option::level_to_str(i
.second
.opt
->level
);
273 tbl
<< i
.second
.raw_value
;
274 tbl
<< (i
.second
.opt
->can_update_at_runtime() ? "" : "*");
275 tbl
<< TextTable::endrow
;
277 f
->open_object_section("option");
278 f
->dump_string("section", s
.first
);
279 i
.second
.dump(f
.get());
285 odata
.append(stringify(tbl
));
290 } else if (prefix
== "config get") {
292 cmd_getval(cmdmap
, "who", who
);
295 if (!entity
.from_str(who
) &&
296 !entity
.from_str(who
+ ".")) {
297 ss
<< "unrecognized entity '" << who
<< "'";
302 map
<string
,string
> crush_location
;
304 if (entity
.is_osd()) {
305 mon
.osdmon()->osdmap
.crush
->get_full_location(who
, &crush_location
);
306 int id
= atoi(entity
.get_id().c_str());
307 const char *c
= mon
.osdmon()->osdmap
.crush
->get_item_class(id
);
311 dout(10) << __func__
<< " crush_location " << crush_location
312 << " class " << device_class
<< dendl
;
315 std::map
<std::string
,pair
<std::string
,const MaskedOption
*>> src
;
316 auto config
= config_map
.generate_entity_map(
319 mon
.osdmon()->osdmap
.crush
.get(),
323 if (cmd_getval(cmdmap
, "key", name
)) {
324 const Option
*opt
= g_conf().find_option(name
);
326 opt
= mon
.mgrmon()->find_module_option(name
);
332 if (opt
->has_flag(Option::FLAG_NO_MON_UPDATE
)) {
333 // handle special options
334 if (name
== "fsid") {
335 odata
.append(stringify(mon
.monmap
->get_fsid()));
340 ss
<< name
<< " is special and cannot be stored by the mon";
343 // get a single value
344 auto p
= config
.find(name
);
345 if (p
!= config
.end()) {
346 odata
.append(p
->second
);
350 if (!entity
.is_client() &&
351 opt
->daemon_value
!= Option::value_t
{}) {
352 odata
.append(Option::to_str(opt
->daemon_value
));
354 odata
.append(Option::to_str(opt
->value
));
358 // dump all (non-default) values for this entity
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
);
368 f
->open_object_section("config");
370 auto p
= config
.begin();
371 auto q
= src
.begin();
372 for (; p
!= config
.end(); ++p
, ++q
) {
373 if (name
.size() && p
->first
!= name
) {
377 tbl
<< q
->second
.first
;
378 tbl
<< q
->second
.second
->mask
.to_str();
379 tbl
<< Option::level_to_str(q
->second
.second
->opt
->level
);
382 tbl
<< (q
->second
.second
->opt
->can_update_at_runtime() ? "" : "*");
383 tbl
<< TextTable::endrow
;
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());
395 odata
.append(stringify(tbl
));
401 } else if (prefix
== "config log") {
403 cmd_getval(cmdmap
, "num", num
);
406 f
->open_array_section("changesets");
408 for (version_t v
= version
; v
> version
- std::min(version
, (version_t
)num
); --v
) {
410 load_changeset(v
, &ch
);
412 f
->dump_object("changeset", ch
);
421 odata
.append(ds
.str());
423 } else if (prefix
== "config generate-minimal-conf") {
425 conf
<< "# minimal ceph.conf for " << mon
.monmap
->get_fsid() << "\n";
428 conf
<< "[global]\n";
429 conf
<< "\tfsid = " << mon
.monmap
->get_fsid() << "\n";
430 conf
<< "\tmon_host = ";
431 for (auto i
= mon
.monmap
->mon_info
.begin();
432 i
!= mon
.monmap
->mon_info
.end();
434 if (i
!= mon
.monmap
->mon_info
.begin()) {
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();
446 conf
<< i
->second
.public_addrs
;
450 conf
<< config_map
.global
.get_minimal_conf();
451 for (auto m
: { &config_map
.by_type
, &config_map
.by_id
}) {
453 auto s
= i
.second
.get_minimal_conf();
455 conf
<< "\n[" << i
.first
<< "]\n" << s
;
459 odata
.append(conf
.str());
466 mon
.reply_command(op
, err
, ss
.str(), odata
, get_last_committed());
470 void ConfigMonitor::handle_get_config(MonOpRequestRef op
)
472 auto m
= op
->get_req
<MGetConfig
>();
473 dout(10) << __func__
<< " " << m
->name
<< " host " << m
->host
<< dendl
;
475 const OSDMap
& osdmap
= mon
.osdmon()->osdmap
;
476 map
<string
,string
> crush_location
;
477 osdmap
.crush
->get_full_location(m
->host
, &crush_location
);
478 auto out
= config_map
.generate_entity_map(
483 dout(20) << " config is " << out
<< dendl
;
484 m
->get_connection()->send_message(new MConfig
{std::move(out
)});
487 bool ConfigMonitor::prepare_update(MonOpRequestRef op
)
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
:
495 return prepare_command(op
);
496 } catch (const bad_cmd_get
& e
) {
498 mon
.reply_command(op
, -EINVAL
, e
.what(), bl
, get_last_committed());
505 bool ConfigMonitor::prepare_command(MonOpRequestRef op
)
507 auto m
= op
->get_req
<MMonCommand
>();
508 std::stringstream ss
;
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
));
519 if (!cmdmap_from_json(m
->cmd
, &cmdmap
, ss
)) {
520 string rs
= ss
.str();
521 mon
.reply_command(op
, -EINVAL
, rs
, get_last_committed());
526 cmd_getval(cmdmap
, "prefix", prefix
);
529 if (prefix
== "config set" ||
530 prefix
== "config rm") {
534 cmd_getval(cmdmap
, "who", who
);
535 cmd_getval(cmdmap
, "name", name
);
536 cmd_getval(cmdmap
, "value", value
);
537 cmd_getval(cmdmap
, "force", force
);
539 if (prefix
== "config set" && !force
) {
540 const Option
*opt
= g_conf().find_option(name
);
542 opt
= mon
.mgrmon()->find_module_option(name
);
545 ss
<< "unrecognized config option '" << name
<< "'";
550 Option::value_t real_value
;
552 err
= opt
->parse_value(value
, &real_value
, &errstr
, &value
);
554 ss
<< "error parsing value: " << errstr
;
558 if (opt
->has_flag(Option::FLAG_NO_MON_UPDATE
)) {
560 ss
<< name
<< " is special and cannot be stored by the mon";
567 if (!ConfigMap::parse_mask(who
, §ion
, &mask
)) {
568 ss
<< "unrecognized config target '" << who
<< "'";
574 if (section
.size()) {
575 key
+= section
+ "/";
579 string mask_str
= mask
.to_str();
580 if (mask_str
.size()) {
581 key
+= mask_str
+ "/";
585 if (prefix
== "config set") {
590 pending
[key
].reset();
593 } else if (prefix
== "config reset") {
594 int64_t revert_to
= -1;
595 cmd_getval(cmdmap
, "num", revert_to
);
597 revert_to
> (int64_t)version
) {
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";
604 if (revert_to
== (int64_t)version
) {
608 for (int64_t v
= version
; v
> revert_to
; --v
) {
610 load_changeset(v
, &ch
);
611 for (auto& i
: ch
.diff
) {
612 if (i
.second
.first
) {
614 bl
.append(*i
.second
.first
);
615 pending
[i
.first
] = bl
;
616 } else if (i
.second
.second
) {
617 pending
[i
.first
].reset();
621 pending_description
= string("reset to ") + stringify(revert_to
);
623 } else if (prefix
== "config assimilate-conf") {
625 bufferlist bl
= m
->get_data();
626 err
= cf
.parse_bufferlist(&bl
, &ss
);
630 bool updated
= false;
631 ostringstream newconf
;
632 for (auto& [section
, s
] : cf
) {
633 dout(20) << __func__
<< " [" << section
<< "]" << dendl
;
634 bool did_section
= false;
635 for (auto& [key
, val
] : s
) {
636 Option::value_t real_value
;
642 // a known and worthy option?
643 const Option
*o
= g_conf().find_option(key
);
645 o
= mon
.mgrmon()->find_module_option(key
);
648 (o
->flags
& Option::FLAG_NO_MON_UPDATE
) ||
649 (o
->flags
& Option::FLAG_CLUSTER_CREATE
)) {
653 err
= o
->parse_value(val
, &real_value
, &errstr
, &value
);
655 dout(20) << __func__
<< " failed to parse " << key
<< " = '"
656 << val
<< "'" << dendl
;
659 // does it conflict with an existing value?
661 const Section
*s
= config_map
.find_section(section
);
663 auto k
= s
->options
.find(key
);
664 if (k
!= s
->options
.end()) {
665 if (value
!= k
->second
.raw_value
) {
666 dout(20) << __func__
<< " have " << key
667 << " = " << k
->second
.raw_value
668 << " (not " << value
<< ")" << dendl
;
671 dout(20) << __func__
<< " already have " << key
672 << " = " << k
->second
.raw_value
<< dendl
;
677 dout(20) << __func__
<< " add " << key
<< " = " << value
678 << " (" << val
<< ")" << dendl
;
682 pending
[section
+ "/" + key
] = bl
;
688 dout(20) << __func__
<< " skip " << key
<< " = " << value
689 << " (" << val
<< ")" << dendl
;
691 newconf
<< "\n[" << section
<< "]\n";
694 newconf
<< "\t" << key
<< " = " << val
<< "\n";
697 odata
.append(newconf
.str());
702 ss
<< "unknown command " << prefix
;
707 mon
.reply_command(op
, err
, ss
.str(), odata
, get_last_committed());
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
) {
717 p
= pending
.erase(p
);
718 } else if (!p
->second
&& q
== current
.end()) {
719 // erasing non-existent value
720 p
= pending
.erase(p
);
725 if (pending
.empty()) {
729 // immediately propose *with* KV mon
730 encode_pending_to_kvmon();
732 mon
.kvmon()->propose_pending();
734 force_immediate_propose();
735 wait_for_finished_proposal(
737 new Monitor::C_Command(
738 mon
, op
, 0, ss
.str(), odata
,
739 get_last_committed() + 1));
743 void ConfigMonitor::tick()
745 if (!is_active() || !mon
.is_leader()) {
748 dout(10) << __func__
<< dendl
;
749 bool changed
= false;
750 if (!pending_cleanup
.empty()) {
753 if (changed
&& mon
.kvmon()->is_writeable()) {
755 encode_pending_to_kvmon();
756 mon
.kvmon()->propose_pending();
762 void ConfigMonitor::on_active()
766 void ConfigMonitor::load_config()
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" },
779 KeyValueDB::Iterator it
= mon
.store
->get_iterator(KV_PREFIX
);
780 it
->lower_bound(KEY_PREFIX
);
783 pending_cleanup
.clear();
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();
789 current
[key
] = it
->value();
793 config_map
.parse_key(key
, &name
, &who
);
795 // has this option been renamed?
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
801 pending_cleanup
[key
].reset();
802 pending_cleanup
[who
+ "/" + p
->second
] = it
->value();
804 // continue loading under the new name
809 const Option
*opt
= g_conf().find_option(name
);
811 opt
= mon
.mgrmon()->find_module_option(name
);
814 dout(10) << __func__
<< " unrecognized option '" << name
<< "'" << dendl
;
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();
822 int r
= opt
->pre_validate(&value
, &err
);
824 dout(10) << __func__
<< " pre-validate failed on '" << name
<< "' = '"
825 << value
<< "' for " << name
<< dendl
;
828 MaskedOption
mopt(opt
);
829 mopt
.raw_value
= value
;
832 !ConfigMap::parse_mask(who
, §ion_name
, &mopt
.mask
)) {
833 derr
<< __func__
<< " invalid mask for key " << key
<< dendl
;
834 pending_cleanup
[key
].reset();
835 } else if (opt
->has_flag(Option::FLAG_NO_MON_UPDATE
)) {
836 dout(10) << __func__
<< " NO_MON_UPDATE option '"
837 << name
<< "' = '" << value
<< "' for " << name
839 pending_cleanup
[key
].reset();
841 if (section_name
.empty()) {
842 // we prefer global/$option instead of just $option
843 derr
<< __func__
<< " adding global/ prefix to key '" << key
<< "'"
845 pending_cleanup
[key
].reset();
846 pending_cleanup
["global/"s
+ key
] = it
->value();
848 Section
*section
= &config_map
.global
;;
849 if (section_name
.size() && section_name
!= "global") {
850 if (section_name
.find('.') != std::string::npos
) {
851 section
= &config_map
.by_id
[section_name
];
853 section
= &config_map
.by_type
[section_name
];
856 section
->options
.insert(make_pair(name
, std::move(mopt
)));
861 dout(10) << __func__
<< " got " << num
<< " keys" << dendl
;
863 // refresh our own config
865 const OSDMap
& osdmap
= mon
.osdmon()->osdmap
;
866 map
<string
,string
> crush_location
;
867 osdmap
.crush
->get_full_location(g_conf()->host
, &crush_location
);
868 auto out
= config_map
.generate_entity_map(
872 string
{}); // no device class
873 g_conf().set_mon_vals(g_ceph_context
, out
, nullptr);
877 void ConfigMonitor::load_changeset(version_t v
, ConfigChangeSet
*ch
)
880 string prefix
= HISTORY_PREFIX
+ stringify(v
) + "/";
881 KeyValueDB::Iterator it
= mon
.store
->get_iterator(KV_PREFIX
);
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();
888 decode(ch
->stamp
, p
);
891 catch (ceph::buffer::error
& e
) {
892 derr
<< __func__
<< " failure decoding changeset " << v
<< dendl
;
895 char op
= it
->key()[prefix
.length()];
896 string key
= it
->key().substr(prefix
.length() + 1);
898 ch
->diff
[key
].first
= it
->value().to_str();
899 } else if (op
== '+') {
900 ch
->diff
[key
].second
= it
->value().to_str();
907 bool ConfigMonitor::refresh_config(MonSession
*s
)
909 const OSDMap
& osdmap
= mon
.osdmon()->osdmap
;
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
;
918 if (s
->name
.is_osd()) {
919 const char *c
= osdmap
.crush
->get_item_class(s
->name
.num());
922 dout(10) << __func__
<< " device_class " << device_class
<< dendl
;
926 dout(20) << __func__
<< " " << s
->entity_name
<< " crush " << crush_location
927 << " device_class " << device_class
<< dendl
;
928 auto out
= config_map
.generate_entity_map(
934 if (out
== s
->last_config
&& s
->any_config
) {
935 dout(20) << __func__
<< " no change, " << out
<< dendl
;
938 // removing this to hide sensitive data going into logs
939 // leaving this for debugging purposes
940 // dout(20) << __func__ << " " << out << dendl;
941 s
->last_config
= std::move(out
);
942 s
->any_config
= true;
946 bool ConfigMonitor::maybe_send_config(MonSession
*s
)
948 bool changed
= refresh_config(s
);
949 dout(10) << __func__
<< " to " << s
->name
<< " "
950 << (changed
? "(changed)" : "(unchanged)")
958 void ConfigMonitor::send_config(MonSession
*s
)
960 dout(10) << __func__
<< " to " << s
->name
<< dendl
;
961 auto m
= new MConfig(s
->last_config
);
962 s
->con
->send_message(m
);
965 void ConfigMonitor::check_sub(MonSession
*s
)
967 if (!s
->authenticated
) {
968 dout(20) << __func__
<< " not authenticated " << s
->entity_name
<< dendl
;
971 auto p
= s
->sub_map
.find("config");
972 if (p
!= s
->sub_map
.end()) {
973 check_sub(p
->second
);
977 void ConfigMonitor::check_sub(Subscription
*sub
)
980 << " next " << sub
->next
981 << " have " << version
<< dendl
;
982 if (sub
->next
<= version
) {
983 maybe_send_config(sub
->session
);
985 mon
.with_session_map([sub
](MonSessionMap
& session_map
) {
986 session_map
.remove_sub(sub
);
989 sub
->next
= version
+ 1;
994 void ConfigMonitor::check_all_subs()
996 dout(10) << __func__
<< dendl
;
997 auto subs
= mon
.session_map
.subs
.find("config");
998 if (subs
== mon
.session_map
.subs
.end()) {
1001 int updated
= 0, total
= 0;
1002 auto p
= subs
->second
->begin();
1007 if (maybe_send_config(sub
->session
)) {
1011 dout(10) << __func__
<< " updated " << updated
<< " / " << total
<< dendl
;