]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/MonmapMonitor.cc
8abcf81312e9d73aae9014f3380cfb28eb82df57
[ceph.git] / ceph / src / mon / MonmapMonitor.cc
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) 2009 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
15 #include "MonmapMonitor.h"
16 #include "Monitor.h"
17 #include "messages/MMonCommand.h"
18 #include "messages/MMonJoin.h"
19
20 #include "common/ceph_argparse.h"
21 #include "common/errno.h"
22 #include <sstream>
23 #include "common/config.h"
24 #include "common/cmdparse.h"
25
26 #include "include/assert.h"
27 #include "include/stringify.h"
28
29 #define dout_subsys ceph_subsys_mon
30 #undef dout_prefix
31 #define dout_prefix _prefix(_dout, mon)
32 static ostream& _prefix(std::ostream *_dout, Monitor *mon) {
33 return *_dout << "mon." << mon->name << "@" << mon->rank
34 << "(" << mon->get_state_name()
35 << ").monmap v" << mon->monmap->epoch << " ";
36 }
37
38 void MonmapMonitor::create_initial()
39 {
40 dout(10) << __func__ << " using current monmap" << dendl;
41 pending_map = *mon->monmap;
42 pending_map.epoch = 1;
43
44 if (g_conf->mon_debug_no_initial_persistent_features) {
45 derr << __func__ << " mon_debug_no_initial_persistent_features=true"
46 << dendl;
47 } else {
48 // initialize with default persistent features for new clusters
49 pending_map.persistent_features = ceph::features::mon::get_persistent();
50 }
51 }
52
53 void MonmapMonitor::update_from_paxos(bool *need_bootstrap)
54 {
55 version_t version = get_last_committed();
56 if (version <= mon->monmap->get_epoch())
57 return;
58
59 dout(10) << __func__ << " version " << version
60 << ", my v " << mon->monmap->epoch << dendl;
61
62 if (need_bootstrap && version != mon->monmap->get_epoch()) {
63 dout(10) << " signaling that we need a bootstrap" << dendl;
64 *need_bootstrap = true;
65 }
66
67 // read and decode
68 monmap_bl.clear();
69 int ret = get_version(version, monmap_bl);
70 assert(ret == 0);
71 assert(monmap_bl.length());
72
73 dout(10) << __func__ << " got " << version << dendl;
74 mon->monmap->decode(monmap_bl);
75
76 if (mon->store->exists("mkfs", "monmap")) {
77 auto t(std::make_shared<MonitorDBStore::Transaction>());
78 t->erase("mkfs", "monmap");
79 mon->store->apply_transaction(t);
80 }
81
82 check_subs();
83 }
84
85 void MonmapMonitor::create_pending()
86 {
87 pending_map = *mon->monmap;
88 pending_map.epoch++;
89 pending_map.last_changed = ceph_clock_now();
90 dout(10) << __func__ << " monmap epoch " << pending_map.epoch << dendl;
91 }
92
93 void MonmapMonitor::encode_pending(MonitorDBStore::TransactionRef t)
94 {
95 dout(10) << __func__ << " epoch " << pending_map.epoch << dendl;
96
97 assert(mon->monmap->epoch + 1 == pending_map.epoch ||
98 pending_map.epoch == 1); // special case mkfs!
99 bufferlist bl;
100 pending_map.encode(bl, mon->get_quorum_con_features());
101
102 put_version(t, pending_map.epoch, bl);
103 put_last_committed(t, pending_map.epoch);
104
105 // generate a cluster fingerprint, too?
106 if (pending_map.epoch == 1) {
107 mon->prepare_new_fingerprint(t);
108 }
109 }
110
111 class C_ApplyFeatures : public Context {
112 MonmapMonitor *svc;
113 mon_feature_t features;
114 public:
115 C_ApplyFeatures(MonmapMonitor *s, const mon_feature_t& f) :
116 svc(s), features(f) { }
117 void finish(int r) override {
118 if (r >= 0) {
119 svc->apply_mon_features(features);
120 } else if (r == -EAGAIN || r == -ECANCELED) {
121 // discard features if we're no longer on the quorum that
122 // established them in the first place.
123 return;
124 } else {
125 assert(0 == "bad C_ApplyFeatures return value");
126 }
127 }
128 };
129
130 void MonmapMonitor::apply_mon_features(const mon_feature_t& features)
131 {
132 if (!is_writeable()) {
133 dout(5) << __func__ << " wait for service to be writeable" << dendl;
134 wait_for_writeable_ctx(new C_ApplyFeatures(this, features));
135 return;
136 }
137
138 assert(is_writeable());
139 assert(features.contains_all(pending_map.persistent_features));
140 // we should never hit this because `features` should be the result
141 // of the quorum's supported features. But if it happens, die.
142 assert(ceph::features::mon::get_supported().contains_all(features));
143
144 mon_feature_t new_features =
145 (pending_map.persistent_features ^
146 (features & ceph::features::mon::get_persistent()));
147
148 if (new_features.empty()) {
149 dout(10) << __func__ << " features match current pending: "
150 << features << dendl;
151 return;
152 }
153
154 if (mon->get_quorum().size() < mon->monmap->size()) {
155 dout(1) << __func__ << " new features " << new_features
156 << " contains features that require a full quorum"
157 << " (quorum size is " << mon->get_quorum().size()
158 << ", requires " << mon->monmap->size() << "): "
159 << new_features
160 << " -- do not enable them!" << dendl;
161 return;
162 }
163
164 new_features |= pending_map.persistent_features;
165
166 dout(5) << __func__ << " applying new features to monmap;"
167 << " had " << pending_map.persistent_features
168 << ", will have " << new_features << dendl;
169 pending_map.persistent_features = new_features;
170 propose_pending();
171 }
172
173 void MonmapMonitor::on_active()
174 {
175 if (get_last_committed() >= 1 && !mon->has_ever_joined) {
176 // make note of the fact that i was, once, part of the quorum.
177 dout(10) << "noting that i was, once, part of an active quorum." << dendl;
178
179 /* This is some form of nasty in-breeding we have between the MonmapMonitor
180 and the Monitor itself. We should find a way to get rid of it given our
181 new architecture. Until then, stick with it since we are a
182 single-threaded process and, truth be told, no one else relies on this
183 thing besides us.
184 */
185 auto t(std::make_shared<MonitorDBStore::Transaction>());
186 t->put(Monitor::MONITOR_NAME, "joined", 1);
187 mon->store->apply_transaction(t);
188 mon->has_ever_joined = true;
189 }
190
191 if (mon->is_leader()) {
192 mon->clog->debug() << "monmap " << *mon->monmap;
193 }
194
195 apply_mon_features(mon->get_quorum_mon_features());
196 }
197
198 bool MonmapMonitor::preprocess_query(MonOpRequestRef op)
199 {
200 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
201 switch (m->get_type()) {
202 // READs
203 case MSG_MON_COMMAND:
204 return preprocess_command(op);
205 case MSG_MON_JOIN:
206 return preprocess_join(op);
207 default:
208 ceph_abort();
209 return true;
210 }
211 }
212
213 void MonmapMonitor::dump_info(Formatter *f)
214 {
215 f->dump_unsigned("monmap_first_committed", get_first_committed());
216 f->dump_unsigned("monmap_last_committed", get_last_committed());
217 f->open_object_section("monmap");
218 mon->monmap->dump(f);
219 f->close_section();
220 f->open_array_section("quorum");
221 for (set<int>::iterator q = mon->get_quorum().begin(); q != mon->get_quorum().end(); ++q)
222 f->dump_int("mon", *q);
223 f->close_section();
224 }
225
226 bool MonmapMonitor::preprocess_command(MonOpRequestRef op)
227 {
228 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
229 int r = -1;
230 bufferlist rdata;
231 stringstream ss;
232
233 map<string, cmd_vartype> cmdmap;
234 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
235 string rs = ss.str();
236 mon->reply_command(op, -EINVAL, rs, rdata, get_last_committed());
237 return true;
238 }
239
240 string prefix;
241 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
242
243 MonSession *session = m->get_session();
244 if (!session) {
245 mon->reply_command(op, -EACCES, "access denied", get_last_committed());
246 return true;
247 }
248
249 string format;
250 cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
251 boost::scoped_ptr<Formatter> f(Formatter::create(format));
252
253 if (prefix == "mon stat") {
254 mon->monmap->print_summary(ss);
255 ss << ", election epoch " << mon->get_epoch() << ", leader "
256 << mon->get_leader() << " " << mon->get_leader_name()
257 << ", quorum " << mon->get_quorum() << " " << mon->get_quorum_names();
258 rdata.append(ss);
259 ss.str("");
260 r = 0;
261
262 } else if (prefix == "mon getmap" ||
263 prefix == "mon dump") {
264
265 epoch_t epoch;
266 int64_t epochnum;
267 cmd_getval(g_ceph_context, cmdmap, "epoch", epochnum, (int64_t)0);
268 epoch = epochnum;
269
270 MonMap *p = mon->monmap;
271 if (epoch) {
272 bufferlist bl;
273 r = get_version(epoch, bl);
274 if (r == -ENOENT) {
275 ss << "there is no map for epoch " << epoch;
276 goto reply;
277 }
278 assert(r == 0);
279 assert(bl.length() > 0);
280 p = new MonMap;
281 p->decode(bl);
282 }
283
284 assert(p != NULL);
285
286 if (prefix == "mon getmap") {
287 p->encode(rdata, m->get_connection()->get_features());
288 r = 0;
289 ss << "got monmap epoch " << p->get_epoch();
290 } else if (prefix == "mon dump") {
291 stringstream ds;
292 if (f) {
293 f->open_object_section("monmap");
294 p->dump(f.get());
295 f->open_array_section("quorum");
296 for (set<int>::iterator q = mon->get_quorum().begin();
297 q != mon->get_quorum().end(); ++q) {
298 f->dump_int("mon", *q);
299 }
300 f->close_section();
301 f->close_section();
302 f->flush(ds);
303 r = 0;
304 } else {
305 p->print(ds);
306 r = 0;
307 }
308 rdata.append(ds);
309 ss << "dumped monmap epoch " << p->get_epoch();
310 }
311 if (p != mon->monmap)
312 delete p;
313
314 } else if (prefix == "mon feature ls") {
315
316 bool list_with_value = false;
317 string with_value;
318 if (cmd_getval(g_ceph_context, cmdmap, "with_value", with_value) &&
319 with_value == "--with-value") {
320 list_with_value = true;
321 }
322
323 MonMap *p = mon->monmap;
324
325 // list features
326 mon_feature_t supported = ceph::features::mon::get_supported();
327 mon_feature_t persistent = ceph::features::mon::get_persistent();
328 mon_feature_t required = p->get_required_features();
329
330 stringstream ds;
331 auto print_feature = [&](mon_feature_t& m_features, const char* m_str) {
332 if (f) {
333 if (list_with_value)
334 m_features.dump_with_value(f.get(), m_str);
335 else
336 m_features.dump(f.get(), m_str);
337 } else {
338 if (list_with_value)
339 m_features.print_with_value(ds);
340 else
341 m_features.print(ds);
342 }
343 };
344
345 if (f) {
346 f->open_object_section("features");
347
348 f->open_object_section("all");
349 print_feature(supported, "supported");
350 print_feature(persistent, "persistent");
351 f->close_section(); // all
352
353 f->open_object_section("monmap");
354 print_feature(p->persistent_features, "persistent");
355 print_feature(p->optional_features, "optional");
356 print_feature(required, "required");
357 f->close_section(); // monmap
358
359 f->close_section(); // features
360 f->flush(ds);
361
362 } else {
363 ds << "all features" << std::endl
364 << "\tsupported: ";
365 print_feature(supported, nullptr);
366 ds << std::endl
367 << "\tpersistent: ";
368 print_feature(persistent, nullptr);
369 ds << std::endl
370 << std::endl;
371
372 ds << "on current monmap (epoch "
373 << p->get_epoch() << ")" << std::endl
374 << "\tpersistent: ";
375 print_feature(p->persistent_features, nullptr);
376 ds << std::endl
377 // omit optional features in plain-text
378 // makes it easier to read, and they're, currently, empty.
379 << "\trequired: ";
380 print_feature(required, nullptr);
381 ds << std::endl;
382 }
383 rdata.append(ds);
384 r = 0;
385 }
386
387 reply:
388 if (r != -1) {
389 string rs;
390 getline(ss, rs);
391
392 mon->reply_command(op, r, rs, rdata, get_last_committed());
393 return true;
394 } else
395 return false;
396 }
397
398
399 bool MonmapMonitor::prepare_update(MonOpRequestRef op)
400 {
401 PaxosServiceMessage *m = static_cast<PaxosServiceMessage*>(op->get_req());
402 dout(7) << __func__ << " " << *m << " from " << m->get_orig_source_inst() << dendl;
403
404 switch (m->get_type()) {
405 case MSG_MON_COMMAND:
406 return prepare_command(op);
407 case MSG_MON_JOIN:
408 return prepare_join(op);
409 default:
410 ceph_abort();
411 }
412
413 return false;
414 }
415
416 bool MonmapMonitor::prepare_command(MonOpRequestRef op)
417 {
418 MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
419 stringstream ss;
420 string rs;
421 int err = -EINVAL;
422
423 map<string, cmd_vartype> cmdmap;
424 if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
425 string rs = ss.str();
426 mon->reply_command(op, -EINVAL, rs, get_last_committed());
427 return true;
428 }
429
430 string prefix;
431 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
432
433 MonSession *session = m->get_session();
434 if (!session) {
435 mon->reply_command(op, -EACCES, "access denied", get_last_committed());
436 return true;
437 }
438
439 /* We should follow the following rules:
440 *
441 * - 'monmap' is the current, consistent version of the monmap
442 * - 'pending_map' is the uncommitted version of the monmap
443 *
444 * All checks for the current state must be made against 'monmap'.
445 * All changes are made against 'pending_map'.
446 *
447 * If there are concurrent operations modifying 'pending_map', please
448 * follow the following rules.
449 *
450 * - if pending_map has already been changed, the second operation must
451 * wait for the proposal to finish and be run again; This is the easiest
452 * path to guarantee correctness but may impact performance (i.e., it
453 * will take longer for the user to get a reply).
454 *
455 * - if the result of the second operation can be guaranteed to be
456 * idempotent, the operation may reply to the user once the proposal
457 * finishes; still needs to wait for the proposal to finish.
458 *
459 * - An operation _NEVER_ returns to the user based on pending state.
460 *
461 * If an operation does not modify current stable monmap, it may be
462 * serialized before current pending map, regardless of any change that
463 * has been made to the pending map -- remember, pending is uncommitted
464 * state, thus we are not bound by it.
465 */
466
467 assert(mon->monmap);
468 MonMap &monmap = *mon->monmap;
469
470
471 /* Please note:
472 *
473 * Adding or removing monitors may lead to loss of quorum.
474 *
475 * Because quorum may be lost, it's important to reply something
476 * to the user, lest she end up waiting forever for a reply. And
477 * no reply will ever be sent until quorum is formed again.
478 *
479 * On the other hand, this means we're leaking uncommitted state
480 * to the user. As such, please be mindful of the reply message.
481 *
482 * e.g., 'adding monitor mon.foo' is okay ('adding' is an on-going
483 * operation and conveys its not-yet-permanent nature); whereas
484 * 'added monitor mon.foo' presumes the action has successfully
485 * completed and state has been committed, which may not be true.
486 */
487
488
489 bool propose = false;
490 if (prefix == "mon add") {
491 string name;
492 cmd_getval(g_ceph_context, cmdmap, "name", name);
493 string addrstr;
494 cmd_getval(g_ceph_context, cmdmap, "addr", addrstr);
495 entity_addr_t addr;
496 bufferlist rdata;
497
498 if (!addr.parse(addrstr.c_str())) {
499 err = -EINVAL;
500 ss << "addr " << addrstr << "does not parse";
501 goto reply;
502 }
503
504 if (addr.get_port() == 0) {
505 ss << "port defaulted to " << CEPH_MON_PORT;
506 addr.set_port(CEPH_MON_PORT);
507 }
508
509 /**
510 * If we have a monitor with the same name and different addr, then EEXIST
511 * If we have a monitor with the same addr and different name, then EEXIST
512 * If we have a monitor with the same addr and same name, then wait for
513 * the proposal to finish and return success.
514 * If we don't have the monitor, add it.
515 */
516
517 err = 0;
518 if (!ss.str().empty())
519 ss << "; ";
520
521 do {
522 if (monmap.contains(name)) {
523 if (monmap.get_addr(name) == addr) {
524 // stable map contains monitor with the same name at the same address.
525 // serialize before current pending map.
526 err = 0; // for clarity; this has already been set above.
527 ss << "mon." << name << " at " << addr << " already exists";
528 goto reply;
529 } else {
530 ss << "mon." << name
531 << " already exists at address " << monmap.get_addr(name);
532 }
533 } else if (monmap.contains(addr)) {
534 // we established on the previous branch that name is different
535 ss << "mon." << monmap.get_name(addr)
536 << " already exists at address " << addr;
537 } else {
538 // go ahead and add
539 break;
540 }
541 err = -EEXIST;
542 goto reply;
543 } while (false);
544
545 /* Given there's no delay between proposals on the MonmapMonitor (see
546 * MonmapMonitor::should_propose()), there is no point in checking for
547 * a mismatch between name and addr on pending_map.
548 *
549 * Once we established the monitor does not exist in the committed state,
550 * we can simply go ahead and add the monitor.
551 */
552
553 pending_map.add(name, addr);
554 pending_map.last_changed = ceph_clock_now();
555 ss << "adding mon." << name << " at " << addr;
556 propose = true;
557 dout(0) << __func__ << " proposing new mon." << name << dendl;
558
559 } else if (prefix == "mon remove" ||
560 prefix == "mon rm") {
561 string name;
562 cmd_getval(g_ceph_context, cmdmap, "name", name);
563 if (!monmap.contains(name)) {
564 err = 0;
565 ss << "mon." << name << " does not exist or has already been removed";
566 goto reply;
567 }
568
569 if (monmap.size() == 1) {
570 err = -EINVAL;
571 ss << "error: refusing removal of last monitor " << name;
572 goto reply;
573 }
574
575 /* At the time of writing, there is no risk of races when multiple clients
576 * attempt to use the same name. The reason is simple but may not be
577 * obvious.
578 *
579 * In a nutshell, we do not collate proposals on the MonmapMonitor. As
580 * soon as we return 'true' below, PaxosService::dispatch() will check if
581 * the service should propose, and - if so - the service will be marked as
582 * 'proposing' and a proposal will be triggered. The PaxosService class
583 * guarantees that once a service is marked 'proposing' no further writes
584 * will be handled.
585 *
586 * The decision on whether the service should propose or not is, in this
587 * case, made by MonmapMonitor::should_propose(), which always considers
588 * the proposal delay being 0.0 seconds. This is key for PaxosService to
589 * trigger the proposal immediately.
590 * 0.0 seconds of delay.
591 *
592 * From the above, there's no point in performing further checks on the
593 * pending_map, as we don't ever have multiple proposals in-flight in
594 * this service. As we've established the committed state contains the
595 * monitor, we can simply go ahead and remove it.
596 *
597 * Please note that the code hinges on all of the above to be true. It
598 * has been true since time immemorial and we don't see a good reason
599 * to make it sturdier at this time - mainly because we don't think it's
600 * going to change any time soon, lest for any bug that may be unwillingly
601 * introduced.
602 */
603
604 entity_addr_t addr = pending_map.get_addr(name);
605 pending_map.remove(name);
606 pending_map.last_changed = ceph_clock_now();
607 ss << "removing mon." << name << " at " << addr
608 << ", there will be " << pending_map.size() << " monitors" ;
609 propose = true;
610 err = 0;
611
612 } else if (prefix == "mon feature set") {
613
614 /* PLEASE NOTE:
615 *
616 * We currently only support setting/unsetting persistent features.
617 * This is by design, given at the moment we still don't have optional
618 * features, and, as such, there is no point introducing an interface
619 * to manipulate them. This allows us to provide a cleaner, more
620 * intuitive interface to the user, modifying solely persistent
621 * features.
622 *
623 * In the future we should consider adding another interface to handle
624 * optional features/flags; e.g., 'mon feature flag set/unset', or
625 * 'mon flag set/unset'.
626 */
627 string feature_name;
628 if (!cmd_getval(g_ceph_context, cmdmap, "feature_name", feature_name)) {
629 ss << "missing required feature name";
630 err = -EINVAL;
631 goto reply;
632 }
633
634 mon_feature_t feature;
635 feature = ceph::features::mon::get_feature_by_name(feature_name);
636 if (feature == ceph::features::mon::FEATURE_NONE) {
637 ss << "unknown feature '" << feature_name << "'";
638 err = -ENOENT;
639 goto reply;
640 }
641
642 string sure;
643 if (!cmd_getval(g_ceph_context, cmdmap, "sure", sure) ||
644 sure != "--yes-i-really-mean-it") {
645 ss << "please specify '--yes-i-really-mean-it' if you "
646 << "really, **really** want to set feature '"
647 << feature << "' in the monmap.";
648 err = -EPERM;
649 goto reply;
650 }
651
652 if (!mon->get_quorum_mon_features().contains_all(feature)) {
653 ss << "current quorum does not support feature '" << feature
654 << "'; supported features: "
655 << mon->get_quorum_mon_features();
656 err = -EINVAL;
657 goto reply;
658 }
659
660 ss << "setting feature '" << feature << "'";
661
662 err = 0;
663 if (monmap.persistent_features.contains_all(feature)) {
664 dout(10) << __func__ << " feature '" << feature
665 << "' already set on monmap; no-op." << dendl;
666 goto reply;
667 }
668
669 pending_map.persistent_features.set_feature(feature);
670 pending_map.last_changed = ceph_clock_now();
671 propose = true;
672
673 dout(1) << __func__ << ss.str() << "; new features will be: "
674 << "persistent = " << pending_map.persistent_features
675 // output optional nevertheless, for auditing purposes.
676 << ", optional = " << pending_map.optional_features << dendl;
677
678 } else {
679 ss << "unknown command " << prefix;
680 err = -EINVAL;
681 }
682
683 reply:
684 getline(ss, rs);
685 mon->reply_command(op, err, rs, get_last_committed());
686 // we are returning to the user; do not propose.
687 return propose;
688 }
689
690 bool MonmapMonitor::preprocess_join(MonOpRequestRef op)
691 {
692 MMonJoin *join = static_cast<MMonJoin*>(op->get_req());
693 dout(10) << __func__ << " " << join->name << " at " << join->addr << dendl;
694
695 MonSession *session = join->get_session();
696 if (!session ||
697 !session->is_capable("mon", MON_CAP_W | MON_CAP_X)) {
698 dout(10) << " insufficient caps" << dendl;
699 return true;
700 }
701
702 if (pending_map.contains(join->name) && !pending_map.get_addr(join->name).is_blank_ip()) {
703 dout(10) << " already have " << join->name << dendl;
704 return true;
705 }
706 if (pending_map.contains(join->addr) && pending_map.get_name(join->addr) == join->name) {
707 dout(10) << " already have " << join->addr << dendl;
708 return true;
709 }
710 return false;
711 }
712 bool MonmapMonitor::prepare_join(MonOpRequestRef op)
713 {
714 MMonJoin *join = static_cast<MMonJoin*>(op->get_req());
715 dout(0) << "adding/updating " << join->name << " at " << join->addr << " to monitor cluster" << dendl;
716 if (pending_map.contains(join->name))
717 pending_map.remove(join->name);
718 if (pending_map.contains(join->addr))
719 pending_map.remove(pending_map.get_name(join->addr));
720 pending_map.add(join->name, join->addr);
721 pending_map.last_changed = ceph_clock_now();
722 return true;
723 }
724
725 bool MonmapMonitor::should_propose(double& delay)
726 {
727 delay = 0.0;
728 return true;
729 }
730
731 void MonmapMonitor::get_health(list<pair<health_status_t, string> >& summary,
732 list<pair<health_status_t, string> > *detail,
733 CephContext *cct) const
734 {
735 int max = mon->monmap->size();
736 int actual = mon->get_quorum().size();
737 if (actual < max) {
738 ostringstream ss;
739 ss << (max-actual) << " mons down, quorum " << mon->get_quorum() << " " << mon->get_quorum_names();
740 summary.push_back(make_pair(HEALTH_WARN, ss.str()));
741 if (detail) {
742 set<int> q = mon->get_quorum();
743 for (int i=0; i<max; i++) {
744 if (q.count(i) == 0) {
745 ostringstream ss;
746 ss << "mon." << mon->monmap->get_name(i) << " (rank " << i
747 << ") addr " << mon->monmap->get_addr(i)
748 << " is down (out of quorum)";
749 detail->push_back(make_pair(HEALTH_WARN, ss.str()));
750 }
751 }
752 }
753 }
754 }
755
756 int MonmapMonitor::get_monmap(bufferlist &bl)
757 {
758 version_t latest_ver = get_last_committed();
759 dout(10) << __func__ << " ver " << latest_ver << dendl;
760
761 if (!mon->store->exists(get_service_name(), stringify(latest_ver)))
762 return -ENOENT;
763
764 int err = get_version(latest_ver, bl);
765 if (err < 0) {
766 dout(1) << __func__ << " error obtaining monmap: "
767 << cpp_strerror(err) << dendl;
768 return err;
769 }
770 return 0;
771 }
772
773 void MonmapMonitor::check_subs()
774 {
775 const string type = "monmap";
776 mon->with_session_map([this, &type](const MonSessionMap& session_map) {
777 auto subs = session_map.subs.find(type);
778 if (subs == session_map.subs.end())
779 return;
780 for (auto sub : *subs->second) {
781 check_sub(sub);
782 }
783 });
784 }
785
786 void MonmapMonitor::check_sub(Subscription *sub)
787 {
788 const auto epoch = mon->monmap->get_epoch();
789 dout(10) << __func__
790 << " monmap next " << sub->next
791 << " have " << epoch << dendl;
792 if (sub->next <= epoch) {
793 mon->send_latest_monmap(sub->session->con.get());
794 if (sub->onetime) {
795 mon->with_session_map([this, sub](MonSessionMap& session_map) {
796 session_map.remove_sub(sub);
797 });
798 } else {
799 sub->next = epoch + 1;
800 }
801 }
802 }