diff options
author | Spencer T Brody <spencer@mongodb.com> | 2015-04-28 17:41:48 -0400 |
---|---|---|
committer | Spencer T Brody <spencer@mongodb.com> | 2015-04-29 15:17:25 -0400 |
commit | 762e0cdc48281414c05ee3ef53277bdf138a4334 (patch) | |
tree | f9cc40354131e0fabe51687c3af7f64e312250ef /src/mongo/db | |
parent | cf56e9bc62cab6cfcb06af83fb7a330ba6bb84e8 (diff) | |
download | mongo-762e0cdc48281414c05ee3ef53277bdf138a4334.tar.gz |
SERVER-18117 Add replset view back to http interface
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/dbwebserver.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/repl/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_set_html_summary.cpp | 221 | ||||
-rw-r--r-- | src/mongo/db/repl/repl_set_html_summary.h | 101 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator.h | 7 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl.h | 5 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_mock.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_mock.h | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/replset_web_handler.cpp | 96 | ||||
-rw-r--r-- | src/mongo/db/repl/topology_coordinator.h | 8 | ||||
-rw-r--r-- | src/mongo/db/repl/topology_coordinator_impl.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/repl/topology_coordinator_impl.h | 1 |
13 files changed, 482 insertions, 2 deletions
diff --git a/src/mongo/db/dbwebserver.cpp b/src/mongo/db/dbwebserver.cpp index 9a919b8967b..435cddffe6f 100644 --- a/src/mongo/db/dbwebserver.cpp +++ b/src/mongo/db/dbwebserver.cpp @@ -376,7 +376,8 @@ namespace { } ss << start(dbname) << h2(dbname); - ss << "<p><a href=\"/_commands\">List all commands</a></p>\n"; + ss << "<p><a href=\"/_commands\">List all commands</a> | \n"; + ss << "<a href=\"/_replSet\">Replica set status</a></p>\n"; { const Command::CommandMap* m = Command::webCommands(); diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index ae6bb14e865..1ad73e7c20b 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -238,6 +238,7 @@ env.Library('replica_set_messages', 'repl_set_heartbeat_args_v1.cpp', 'repl_set_heartbeat_response.cpp', 'repl_set_heartbeat_response_v1.cpp', + 'repl_set_html_summary.cpp', 'replica_set_config.cpp', 'replica_set_tag.cpp', 'update_position_args.cpp', diff --git a/src/mongo/db/repl/repl_set_html_summary.cpp b/src/mongo/db/repl/repl_set_html_summary.cpp new file mode 100644 index 00000000000..465bfdeb95e --- /dev/null +++ b/src/mongo/db/repl/repl_set_html_summary.cpp @@ -0,0 +1,221 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kReplication + +#include "mongo/platform/basic.h" + +#include "mongo/db/repl/repl_set_html_summary.h" + +#include <string> +#include <sstream> + +#include "mongo/util/mongoutils/html.h" +#include "mongo/util/mongoutils/str.h" + + +namespace mongo { +namespace repl { + + ReplSetHtmlSummary::ReplSetHtmlSummary() : _selfIndex(-1), _primaryIndex(-1), _selfUptime(0) {} + +namespace { + + /** + * Turns an unsigned int representing a duration of time in milliseconds and turns it into + * a human readable time string representation. + */ + std::string ago(unsigned int duration) { + std::stringstream s; + if( duration < 180 ) { + s << duration << " sec"; + if( duration != 1 ) s << 's'; + } + else if( duration < 3600 ) { + s.precision(2); + s << duration / 60.0 << " mins"; + } + else { + s.precision(2); + s << duration / 3600.0 << " hrs"; + } + return s.str(); + } + + unsigned int timeDifference(Date_t now, Date_t past) { + return static_cast<unsigned int> ((past ? + (now - past) / 1000 /* convert millis to secs */ : 0)); + } + + std::string stateAsHtml(const MemberState& s) { + using namespace html; + + if( s.s == MemberState::RS_STARTUP ) + return a("", + "server still starting up, or still trying to initiate the set", + "STARTUP"); + if( s.s == MemberState::RS_PRIMARY ) + return a("", "this server thinks it is primary", "PRIMARY"); + if( s.s == MemberState::RS_SECONDARY ) + return a("", "this server thinks it is a secondary (slave mode)", "SECONDARY"); + if( s.s == MemberState::RS_RECOVERING ) + return a("", + "recovering/resyncing; after recovery usually auto-transitions to secondary", + "RECOVERING"); + if( s.s == MemberState::RS_STARTUP2 ) + return a("", "loaded config, still determining who is primary", "STARTUP2"); + if( s.s == MemberState::RS_ARBITER ) + return a("", "this server is an arbiter only", "ARBITER"); + if( s.s == MemberState::RS_DOWN ) + return a("", "member is down, slow, or unreachable", "DOWN"); + if( s.s == MemberState::RS_ROLLBACK ) + return a("", "rolling back operations to get in sync", "ROLLBACK"); + if( s.s == MemberState::RS_UNKNOWN) + return a("", "we do not know what state this node is in", "UNKNOWN"); + if( s.s == MemberState::RS_REMOVED) + return a("", "this server has been removed from the replica set config", "ROLLBACK"); + return ""; + } +} + + const std::string ReplSetHtmlSummary::toHtmlString() const { + using namespace html; + + std::stringstream s; + + if (!_config.isInitialized()) { + s << p("Still starting up, or else replset is not yet initiated."); + return s.str(); + } + if (_selfIndex < 0) { + s << p("This node is not a member of its replica set configuration, it most likely was" + " removed recently"); + return s.str(); + } + + int votesUp = 0; + int totalVotes = 0; + // Build table of node information. + std::stringstream memberTable; + const char *h[] = + {"Member", + "<a title=\"member id in the replset config\">id</a>", + "Up", + "<a title=\"length of time we have been continuously connected to the other member " + "with no reconnects (for self, shows uptime)\">cctime</a>", + "<a title=\"when this server last received a heartbeat response - includes error code " + "responses\">Last heartbeat</a>", + "Votes", + "Priority", + "State", + "Messages", + "<a title=\"how up to date this server is. this value polled every few seconds so " + "actually lag is typically lower than value shown here.\">optime</a>", + 0 + }; + memberTable << table(h); + + for (int i = 0; i < _config.getNumMembers(); ++i) { + const MemberConfig& memberConfig = _config.getMemberAt(i); + const MemberHeartbeatData& memberHB = _hbData[i]; + bool isSelf = _selfIndex == i; + bool up = memberHB.getHealth() > 0; + + totalVotes += memberConfig.getNumVotes(); + if (up || isSelf) { + votesUp += memberConfig.getNumVotes(); + } + + memberTable << tr(); + if (isSelf) { + memberTable << td(memberConfig.getHostAndPort().toString() + " (me)"); + memberTable << td(memberConfig.getId()); + memberTable << td("1"); // up + memberTable << td(ago(_selfUptime)); + memberTable << td(""); // last heartbeat + memberTable << td(std::to_string(memberConfig.getNumVotes())); + memberTable << td(std::to_string(memberConfig.getPriority())); + memberTable << td(stateAsHtml(_selfState) + + (memberConfig.isHidden() ? " (hidden)" : "")); + memberTable << td(_selfHeartbeatMessage); + memberTable << td(_selfOptime.toString()); + } + else { + std::stringstream link; + link << "http://" << memberConfig.getHostAndPort().host() << ':' << + (memberConfig.getHostAndPort().port() + 1000) << "/_replSet"; + memberTable << td( a(link.str(), "", memberConfig.getHostAndPort().toString()) ); + memberTable << td(memberConfig.getId()); + memberTable << td(red(str::stream() << memberHB.getHealth(), !up)); + const unsigned int uptime = timeDifference(_now, memberHB.getUpSince()); + memberTable << td(ago(uptime)); + if (memberHB.getLastHeartbeat() == 0) { + memberTable << td("never"); + } + else { + memberTable << td(ago(timeDifference(_now, memberHB.getLastHeartbeat()))); + } + memberTable << td(std::to_string(memberConfig.getNumVotes())); + memberTable << td(std::to_string(memberConfig.getPriority())); + std::string state = memberHB.getState().toString() + + (memberConfig.isHidden() ? " (hidden)" : ""); + if (up) { + memberTable << td(state); + } + else { + memberTable << td( grey(str::stream() << "(was " << state << ')', true) ); + } + memberTable << td(grey(memberHB.getLastHeartbeatMsg(), !up)); + memberTable << td(memberHB.getLastHeartbeat() == 0 ? + "?" : memberHB.getOpTime().toString()); + } + memberTable << _tr(); + } + memberTable << _table(); + + s << table(0, false); + s << tr("Set name:", _config.getReplSetName()); + bool majorityUp = votesUp * 2 > totalVotes; + s << tr("Majority up:", majorityUp ? "yes" : "no" ); + + const MemberConfig& selfConfig = _config.getMemberAt(_selfIndex); + + if (_primaryIndex >= 0 && _primaryIndex != _selfIndex && !selfConfig.isArbiter()) { + int lag = _hbData[_primaryIndex].getOpTime().getSecs() - _selfOptime.getSecs(); + s << tr("Lag: ", str::stream() << lag << " secs"); + } + + s << _table(); + + s << memberTable.str(); + + return s.str(); + } + +} // namespace repl +} // namespace mongo diff --git a/src/mongo/db/repl/repl_set_html_summary.h b/src/mongo/db/repl/repl_set_html_summary.h new file mode 100644 index 00000000000..463a2bcdc53 --- /dev/null +++ b/src/mongo/db/repl/repl_set_html_summary.h @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <string> +#include <vector> + +#include "mongo/db/repl/member_heartbeat_data.h" +#include "mongo/db/repl/replica_set_config.h" + +namespace mongo { + +namespace repl { + + /** + * Class containing all the information needed to build the replSet page on http interface, + * and the logic to generate that page. + */ + class ReplSetHtmlSummary { + public: + ReplSetHtmlSummary(); + + const std::string toHtmlString() const; + + void setConfig(const ReplicaSetConfig& config) { + _config = config; + } + + void setHBData(const std::vector<MemberHeartbeatData>& hbData) { + _hbData = hbData; + } + + void setSelfIndex(int index) { + _selfIndex = index; + } + + void setPrimaryIndex(int index) { + _primaryIndex = index; + } + + void setSelfOptime(const Timestamp& ts) { + _selfOptime = ts; + } + + void setSelfUptime(unsigned int time) { + _selfUptime = time; + } + + void setNow(Date_t now) { + _now = now; + } + + void setSelfState(const MemberState& state) { + _selfState = state; + } + + void setSelfHeartbeatMessage(StringData msg) { + _selfHeartbeatMessage = msg.toString(); + } + + private: + + ReplicaSetConfig _config; + std::vector<MemberHeartbeatData> _hbData; + Date_t _now; + int _selfIndex; + int _primaryIndex; + Timestamp _selfOptime; + unsigned int _selfUptime; + MemberState _selfState; + std::string _selfHeartbeatMessage; + }; + +} // namespace repl +} // namespace mongo diff --git a/src/mongo/db/repl/replication_coordinator.h b/src/mongo/db/repl/replication_coordinator.h index 2b176e29a10..50945a1f4be 100644 --- a/src/mongo/db/repl/replication_coordinator.h +++ b/src/mongo/db/repl/replication_coordinator.h @@ -60,6 +60,7 @@ namespace repl { class ReplSetHeartbeatArgsV1; class ReplSetHeartbeatResponse; class ReplSetHeartbeatResponseV1; + class ReplSetHtmlSummary; class ReplSetRequestVotesArgs; class ReplSetRequestVotesResponse; class ReplicaSetConfig; @@ -590,6 +591,12 @@ namespace repl { */ virtual bool isV1ElectionProtocol() = 0; + /** + * Writes into 'output' all the information needed to generate a summary of the current + * replication state for use by the web interface. + */ + virtual void summarizeAsHtml(ReplSetHtmlSummary* output) = 0; + protected: ReplicationCoordinator(); diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index fd617a9cbe8..bc137be8ddc 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -48,6 +48,7 @@ #include "mongo/db/repl/repl_client_info.h" #include "mongo/db/repl/repl_set_heartbeat_args.h" #include "mongo/db/repl/repl_set_heartbeat_response.h" +#include "mongo/db/repl/repl_set_html_summary.h" #include "mongo/db/repl/repl_set_request_votes_args.h" #include "mongo/db/repl/repl_settings.h" #include "mongo/db/repl/replica_set_config_checks.h" @@ -2442,5 +2443,31 @@ namespace { return {ErrorCodes::CommandNotFound, "not implemented"}; } + void ReplicationCoordinatorImpl::summarizeAsHtml(ReplSetHtmlSummary* output) { + CBHStatus cbh = _replExecutor.scheduleWork( + stdx::bind(&ReplicationCoordinatorImpl::_summarizeAsHtml_finish, + this, + stdx::placeholders::_1, + output)); + if (cbh.getStatus() == ErrorCodes::ShutdownInProgress) { + return; + } + fassert(28638, cbh.getStatus()); + _replExecutor.wait(cbh.getValue()); + } + + void ReplicationCoordinatorImpl::_summarizeAsHtml_finish(const CallbackData& cbData, + ReplSetHtmlSummary* output) { + if (cbData.status == ErrorCodes::CallbackCanceled) { + return; + } + + output->setSelfOptime(getMyLastOptime()); + output->setSelfUptime(time(0) - serverGlobalParams.started); + output->setNow(_replExecutor.now()); + + _topCoord->summarizeAsHtml(output); + } + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/replication_coordinator_impl.h b/src/mongo/db/repl/replication_coordinator_impl.h index 36d8398ac62..5304f7134df 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.h +++ b/src/mongo/db/repl/replication_coordinator_impl.h @@ -251,6 +251,8 @@ namespace repl { virtual bool isV1ElectionProtocol(); + virtual void summarizeAsHtml(ReplSetHtmlSummary* s); + // ================== Test support API =================== /** @@ -778,6 +780,9 @@ namespace repl { */ void _updateLastCommittedOpTime_inlock(); + void _summarizeAsHtml_finish(const ReplicationExecutor::CallbackData& cbData, + ReplSetHtmlSummary* output); + // // All member variables are labeled with one of the following codes indicating the // synchronization rules for accessing them. diff --git a/src/mongo/db/repl/replication_coordinator_mock.cpp b/src/mongo/db/repl/replication_coordinator_mock.cpp index 63a917470fa..2fae22cd016 100644 --- a/src/mongo/db/repl/replication_coordinator_mock.cpp +++ b/src/mongo/db/repl/replication_coordinator_mock.cpp @@ -322,5 +322,7 @@ namespace repl { return true; } + void ReplicationCoordinatorMock::summarizeAsHtml(ReplSetHtmlSummary* output) {} + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/replication_coordinator_mock.h b/src/mongo/db/repl/replication_coordinator_mock.h index e8615b7c985..70fb82a946e 100644 --- a/src/mongo/db/repl/replication_coordinator_mock.h +++ b/src/mongo/db/repl/replication_coordinator_mock.h @@ -196,6 +196,8 @@ namespace repl { virtual bool isV1ElectionProtocol(); + virtual void summarizeAsHtml(ReplSetHtmlSummary* output); + private: const ReplSettings _settings; diff --git a/src/mongo/db/repl/replset_web_handler.cpp b/src/mongo/db/repl/replset_web_handler.cpp new file mode 100644 index 00000000000..12fe6ab8bd0 --- /dev/null +++ b/src/mongo/db/repl/replset_web_handler.cpp @@ -0,0 +1,96 @@ +/** +* Copyright (C) 2015 10gen Inc. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License, version 3, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#include "mongo/platform/basic.h" + +#include <sstream> + +#include "mongo/db/dbwebserver.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/repl/replication_coordinator_global.h" +#include "mongo/db/repl/repl_set_html_summary.h" +#include "mongo/db/repl/rslog.h" +#include "mongo/util/mongoutils/html.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { +namespace repl { + + using namespace html; + + class ReplSetHandler : public DbWebHandler { + public: + ReplSetHandler() : DbWebHandler( "_replSet" , 1 , false ) {} + + virtual bool handles( const std::string& url ) const { + return str::startsWith( url , "/_replSet" ); + } + + virtual void handle( OperationContext* txn, + const char *rq, + const std::string& url, + BSONObj params, + std::string& responseMsg, + int& responseCode, + std::vector<std::string>& headers, + const SockAddr &from ) { + responseMsg = _replSet(txn); + responseCode = 200; + } + + /* /_replSet show replica set status in html format */ + std::string _replSet(OperationContext* txn) { + std::stringstream s; + s << start("Replica Set Status " + prettyHostName()); + s << p( a("/", "back", "Home") + " | " + + a("/local/system.replset/?html=1", "", "View Replset Config") + " | " + + a("/replSetGetStatus?text=1", "", "replSetGetStatus") + " | " + + a("http://dochub.mongodb.org/core/replicasets", "", "Docs") + ); + + ReplicationCoordinator* replCoord = getGlobalReplicationCoordinator(); + if (replCoord->getReplicationMode() != ReplicationCoordinator::modeReplSet) { + s << p("Not using --replSet"); + s << _end(); + return s.str(); + } + + ReplSetHtmlSummary summary; + replCoord->summarizeAsHtml(&summary); + s << summary.toHtmlString(); + + s << p("Recent replset log activity:"); + fillRsLog(&s); + s << _end(); + return s.str(); + } + + } replSetHandler; + +} // namespace repl +} // namespace mongo diff --git a/src/mongo/db/repl/topology_coordinator.h b/src/mongo/db/repl/topology_coordinator.h index fb6bde87e71..3c97e64be29 100644 --- a/src/mongo/db/repl/topology_coordinator.h +++ b/src/mongo/db/repl/topology_coordinator.h @@ -40,7 +40,6 @@ namespace mongo { - class OperationContext; class Timestamp; namespace repl { @@ -361,6 +360,13 @@ namespace repl { virtual void prepareCursorResponseInfo(BSONObjBuilder* objBuilder, const Timestamp& lastCommittedOpTime) const = 0; + /** + * Writes into 'output' all the information needed to generate a summary of the current + * replication state for use by the web interface. + */ + virtual void summarizeAsHtml(ReplSetHtmlSummary* output) = 0; + + protected: TopologyCoordinator() {} }; diff --git a/src/mongo/db/repl/topology_coordinator_impl.cpp b/src/mongo/db/repl/topology_coordinator_impl.cpp index 046710fa64b..d6dd5128375 100644 --- a/src/mongo/db/repl/topology_coordinator_impl.cpp +++ b/src/mongo/db/repl/topology_coordinator_impl.cpp @@ -40,6 +40,7 @@ #include "mongo/db/repl/isself.h" #include "mongo/db/repl/repl_set_heartbeat_args.h" #include "mongo/db/repl/repl_set_heartbeat_response.h" +#include "mongo/db/repl/repl_set_html_summary.h" #include "mongo/db/repl/replication_executor.h" #include "mongo/db/repl/rslog.h" #include "mongo/db/server_parameters.h" @@ -2093,5 +2094,14 @@ namespace { objBuilder->append("primaryId", _rsConfig.getMemberAt(_currentPrimaryIndex).getId()); } + void TopologyCoordinatorImpl::summarizeAsHtml(ReplSetHtmlSummary* output) { + output->setConfig(_rsConfig); + output->setHBData(_hbdata); + output->setSelfIndex(_selfIndex); + output->setPrimaryIndex(_currentPrimaryIndex); + output->setSelfState(getMemberState()); + output->setSelfHeartbeatMessage(_hbmsg); + } + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/topology_coordinator_impl.h b/src/mongo/db/repl/topology_coordinator_impl.h index e0cd74f7012..f421be325fd 100644 --- a/src/mongo/db/repl/topology_coordinator_impl.h +++ b/src/mongo/db/repl/topology_coordinator_impl.h @@ -189,6 +189,7 @@ namespace repl { virtual void prepareCursorResponseInfo(BSONObjBuilder* objBuilder, const Timestamp& lastCommitttedOpTime) const; + virtual void summarizeAsHtml(ReplSetHtmlSummary* output); //////////////////////////////////////////////////////////// // |