summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2018-05-01 13:42:15 -0400
committerSara Golemon <sara.golemon@mongodb.com>2018-05-01 14:43:47 -0400
commit97245f7c13ee8d18e3d82fc87dd44b9fd72b4da3 (patch)
treed512f78f20b7fd1acb43ecbd09b28883be8fc963
parent52c1fe02a0cd0f64a4d97f7c1e17792b38c67ca4 (diff)
downloadmongo-97245f7c13ee8d18e3d82fc87dd44b9fd72b4da3.tar.gz
SERVER-34229 Add free monitoring to serverStatus
-rw-r--r--jstests/free_mon/free_mon_http_down.js4
-rw-r--r--jstests/free_mon/free_mon_server_status.js48
-rw-r--r--src/mongo/db/free_mon/SConscript2
-rw-r--r--src/mongo/db/free_mon/free_mon_controller.cpp7
-rw-r--r--src/mongo/db/free_mon/free_mon_controller.h7
-rw-r--r--src/mongo/db/free_mon/free_mon_processor.cpp173
-rw-r--r--src/mongo/db/free_mon/free_mon_processor.h37
-rw-r--r--src/mongo/db/free_mon/free_mon_status.cpp64
8 files changed, 268 insertions, 74 deletions
diff --git a/jstests/free_mon/free_mon_http_down.js b/jstests/free_mon/free_mon_http_down.js
index aff229614c4..019b50f23eb 100644
--- a/jstests/free_mon/free_mon_http_down.js
+++ b/jstests/free_mon/free_mon_http_down.js
@@ -17,9 +17,13 @@ load("jstests/free_mon/libs/free_mon.js");
const conn = MongoRunner.runMongod(options);
assert.neq(null, conn, 'mongod was unable to start up');
+ const admin = conn.getDB('admin');
mock_web.waitRegisters(3);
+ const freeMonStats = assert.commandWorked(admin.runCommand({serverStatus: 1})).freeMonitoring;
+ assert.gte(freeMonStats.registerErrors, 3);
+
MongoRunner.stopMongod(conn);
mock_web.stop();
diff --git a/jstests/free_mon/free_mon_server_status.js b/jstests/free_mon/free_mon_server_status.js
new file mode 100644
index 00000000000..5a7fc991831
--- /dev/null
+++ b/jstests/free_mon/free_mon_server_status.js
@@ -0,0 +1,48 @@
+// Validate serverStatus output.
+//
+load("jstests/free_mon/libs/free_mon.js");
+
+(function() {
+ 'use strict';
+
+ const mock_web = new FreeMonWebServer();
+ mock_web.start();
+
+ const mongod = MongoRunner.runMongod({
+ setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(),
+ });
+ assert.neq(mongod, null, 'mongod not running');
+ const admin = mongod.getDB('admin');
+
+ const kRetryIntervalSecs = 1;
+ function freeMonStats() {
+ return assert.commandWorked(admin.runCommand({serverStatus: 1})).freeMonitoring;
+ }
+
+ // Initial state.
+ assert.eq(freeMonStats().state, 'undecided');
+
+ assert.commandWorked(admin.runCommand({setFreeMonitoring: 1, action: 'enable'}));
+ WaitForRegistration(mongod);
+
+ const enabled = freeMonStats();
+ assert.eq(enabled.state, 'enabled');
+ assert.eq(enabled.retryIntervalSecs, kRetryIntervalSecs);
+ assert.eq(enabled.registerErrors, 0);
+ assert.eq(enabled.metricsErrors, 0);
+
+ // Explicitly disabled.
+ assert.commandWorked(admin.runCommand({setFreeMonitoring: 1, action: 'disable'}));
+ sleep(2); // Give the async command time to run.
+
+ const disabled = freeMonStats();
+ assert.eq(disabled.state, 'disabled');
+ assert.eq(disabled.retryIntervalSecs, kRetryIntervalSecs);
+ assert.eq(disabled.registerErrors, 0);
+ assert.eq(disabled.metricsErrors, 0);
+
+ // Enabled.
+ // Cleanup.
+ MongoRunner.stopMongod(mongod);
+ mock_web.stop();
+})();
diff --git a/src/mongo/db/free_mon/SConscript b/src/mongo/db/free_mon/SConscript
index 72d656dce15..ac990e7e8aa 100644
--- a/src/mongo/db/free_mon/SConscript
+++ b/src/mongo/db/free_mon/SConscript
@@ -36,10 +36,12 @@ if free_monitoring == "on":
'free_mon_commands.cpp',
'free_mon_mongod.cpp',
'free_mon_options.cpp',
+ 'free_mon_status.cpp',
'http_client_curl.cpp' if not env.TargetOSIs('windows') else 'http_client_winhttp.cpp',
],
LIBDEPS=[
'free_mon',
+ '$BUILD_DIR/mongo/db/commands/server_status',
'$BUILD_DIR/mongo/db/ftdc/ftdc_server',
'$BUILD_DIR/mongo/util/options_parser/options_parser',
],
diff --git a/src/mongo/db/free_mon/free_mon_controller.cpp b/src/mongo/db/free_mon/free_mon_controller.cpp
index 21200304349..289e8c69d1a 100644
--- a/src/mongo/db/free_mon/free_mon_controller.cpp
+++ b/src/mongo/db/free_mon/free_mon_controller.cpp
@@ -196,4 +196,11 @@ void FreeMonController::turnCrankForTest(size_t countMessagesToIgnore) {
_processor->turnCrankForTest(countMessagesToIgnore);
}
+void FreeMonController::getServerStatus(OperationContext* opCtx, BSONObjBuilder* status) {
+ if (!_processor) {
+ status->append("state", "disabled");
+ }
+ _processor->getServerStatus(opCtx, status);
+}
+
} // namespace mongo
diff --git a/src/mongo/db/free_mon/free_mon_controller.h b/src/mongo/db/free_mon/free_mon_controller.h
index e78dea57d34..3f1807b7e6f 100644
--- a/src/mongo/db/free_mon/free_mon_controller.h
+++ b/src/mongo/db/free_mon/free_mon_controller.h
@@ -35,6 +35,7 @@
#include <vector>
#include "mongo/base/status.h"
+#include "mongo/db/client.h"
#include "mongo/db/free_mon/free_mon_message.h"
#include "mongo/db/free_mon/free_mon_network.h"
#include "mongo/db/free_mon/free_mon_processor.h"
@@ -116,8 +117,10 @@ public:
*/
boost::optional<Status> unregisterServerCommand(Milliseconds timeout);
- // TODO - add these methods
- // void getServerStatus(BSONObjBuilder* builder);
+ /**
+ * Populates an info blob for use by {serverStatus: 1}
+ */
+ void getServerStatus(OperationContext* opCtx, BSONObjBuilder* status);
/**
* Notify on upsert.
diff --git a/src/mongo/db/free_mon/free_mon_processor.cpp b/src/mongo/db/free_mon/free_mon_processor.cpp
index 04620d1cf23..9a6ca23513a 100644
--- a/src/mongo/db/free_mon/free_mon_processor.cpp
+++ b/src/mongo/db/free_mon/free_mon_processor.cpp
@@ -126,12 +126,12 @@ FreeMonProcessor::FreeMonProcessor(FreeMonCollectorCollection& registration,
_metrics(metrics),
_network(network),
_random(Date_t::now().asInt64()),
- _registrationRetry(_random),
- _metricsRetry(_random),
+ _registrationRetry(RegistrationRetryCounter(_random)),
+ _metricsRetry(MetricsRetryCounter(_random)),
_metricsGatherInterval(kDefaultMetricsGatherInterval),
_queue(useCrankForTest) {
- _registrationRetry.reset();
- _metricsRetry.reset();
+ _registrationRetry->reset();
+ _metricsRetry->reset();
}
void FreeMonProcessor::enqueue(std::shared_ptr<FreeMonMessage> msg) {
@@ -262,11 +262,8 @@ void FreeMonProcessor::run() {
}
}
-void FreeMonProcessor::readState(Client* client) {
-
- auto optCtx = client->makeOperationContext();
-
- auto state = FreeMonStorage::read(optCtx.get());
+void FreeMonProcessor::readState(OperationContext* opCtx) {
+ auto state = FreeMonStorage::read(opCtx);
_lastReadState = state;
@@ -276,15 +273,21 @@ void FreeMonProcessor::readState(Client* client) {
_state = state.get();
} else if (!state.is_initialized()) {
// Default the state
- _state.setVersion(kProtocolVersion);
- _state.setState(StorageStateEnum::disabled);
- _state.setRegistrationId("");
- _state.setInformationalURL("");
- _state.setMessage("");
- _state.setUserReminder("");
+ auto state = _state.synchronize();
+ state->setVersion(kProtocolVersion);
+ state->setState(StorageStateEnum::disabled);
+ state->setRegistrationId("");
+ state->setInformationalURL("");
+ state->setMessage("");
+ state->setUserReminder("");
}
}
+void FreeMonProcessor::readState(Client* client) {
+ auto opCtx = client->makeOperationContext();
+ readState(opCtx.get());
+}
+
void FreeMonProcessor::writeState(Client* client) {
// Do a compare and swap
@@ -292,7 +295,7 @@ void FreeMonProcessor::writeState(Client* client) {
// If the local document is different, then oh-well we do nothing, and wait until the next round
// Has our in-memory state changed, if so consider writing
- if (_lastReadState != _state) {
+ if (_lastReadState != _state.get()) {
// The read and write are bound the same operation context
{
@@ -302,9 +305,9 @@ void FreeMonProcessor::writeState(Client* client) {
// If our in-memory copy matches the last read, then write it to disk
if (state == _lastReadState) {
- FreeMonStorage::replace(optCtx.get(), _state);
+ FreeMonStorage::replace(optCtx.get(), _state.get());
- _lastReadState = _state;
+ _lastReadState = boost::make_optional(_state.get());
}
}
}
@@ -401,8 +404,9 @@ void FreeMonProcessor::doCommandRegister(Client* client,
FreeMonRegistrationRequest req;
- if (!_state.getRegistrationId().empty()) {
- req.setId(_state.getRegistrationId());
+ auto regid = _state->getRegistrationId();
+ if (!regid.empty()) {
+ req.setId(regid);
}
req.setVersion(kProtocolVersion);
@@ -422,7 +426,7 @@ void FreeMonProcessor::doCommandRegister(Client* client,
req.setPayload(std::get<0>(collect));
// Record that the registration is pending
- _state.setState(StorageStateEnum::pending);
+ _state->setState(StorageStateEnum::pending);
writeState(client);
@@ -590,7 +594,7 @@ void FreeMonProcessor::doAsyncRegisterComplete(
// Our request is no longer in-progress so delete it
_futureRegistrationResponse.reset();
- if (_state.getState() != StorageStateEnum::pending) {
+ if (_state->getState() != StorageStateEnum::pending) {
notifyPendingRegisters(Status(ErrorCodes::BadValue, "Registration was canceled"));
return;
@@ -603,7 +607,7 @@ void FreeMonProcessor::doAsyncRegisterComplete(
warning() << "Free Monitoring registration halted due to " << s;
// Disable on any error
- _state.setState(StorageStateEnum::disabled);
+ _state->setState(StorageStateEnum::disabled);
// Persist state
writeState(client);
@@ -615,26 +619,29 @@ void FreeMonProcessor::doAsyncRegisterComplete(
}
// Update in-memory state
- _registrationRetry.setMin(Seconds(resp.getReportingInterval()));
+ _registrationRetry->setMin(Seconds(resp.getReportingInterval()));
- _state.setRegistrationId(resp.getId());
+ {
+ auto state = _state.synchronize();
+ state->setRegistrationId(resp.getId());
- if (resp.getUserReminder().is_initialized()) {
- _state.setUserReminder(resp.getUserReminder().get());
- } else {
- _state.setUserReminder("");
- }
+ if (resp.getUserReminder().is_initialized()) {
+ state->setUserReminder(resp.getUserReminder().get());
+ } else {
+ state->setUserReminder("");
+ }
- _state.setMessage(resp.getMessage());
- _state.setInformationalURL(resp.getInformationalURL());
+ state->setMessage(resp.getMessage());
+ state->setInformationalURL(resp.getInformationalURL());
- _state.setState(StorageStateEnum::enabled);
+ state->setState(StorageStateEnum::enabled);
+ }
// Persist state
writeState(client);
// Reset retry counter
- _registrationRetry.reset();
+ _registrationRetry->reset();
// Notify waiters
notifyPendingRegisters(Status::OK());
@@ -643,7 +650,7 @@ void FreeMonProcessor::doAsyncRegisterComplete(
// Enqueue next metrics upload
enqueue(FreeMonMessage::createWithDeadline(FreeMonMessageType::MetricsSend,
- _registrationRetry.getNextDeadline(client)));
+ _registrationRetry->getNextDeadline(client)));
}
void FreeMonProcessor::doAsyncRegisterFail(
@@ -652,32 +659,32 @@ void FreeMonProcessor::doAsyncRegisterFail(
// Our request is no longer in-progress so delete it
_futureRegistrationResponse.reset();
- if (_state.getState() != StorageStateEnum::pending) {
+ if (_state->getState() != StorageStateEnum::pending) {
notifyPendingRegisters(Status(ErrorCodes::BadValue, "Registration was canceled"));
return;
}
- if (!_registrationRetry.incrementError()) {
+ if (!_registrationRetry->incrementError()) {
// We have exceeded our retry
warning() << "Free Monitoring is abandoning registration after excess retries";
return;
}
LOG(1) << "Free Monitoring Registration Failed with status '" << msg->getPayload()
- << "', retrying in " << _registrationRetry.getNextDuration();
+ << "', retrying in " << _registrationRetry->getNextDuration();
// Enqueue a register retry
enqueue(FreeMonRegisterCommandMessage::createWithDeadline(
- _tags, _registrationRetry.getNextDeadline(client)));
+ _tags, _registrationRetry->getNextDeadline(client)));
}
void FreeMonProcessor::doCommandUnregister(
Client* client, FreeMonWaitableMessageWithPayload<FreeMonMessageType::UnregisterCommand>* msg) {
// Treat this request as idempotent
- if (_state.getState() != StorageStateEnum::disabled) {
+ if (_state->getState() != StorageStateEnum::disabled) {
- _state.setState(StorageStateEnum::disabled);
+ _state->setState(StorageStateEnum::disabled);
writeState(client);
@@ -726,24 +733,26 @@ std::string compressMetrics(MetricsBuffer& buffer) {
void FreeMonProcessor::doMetricsSend(Client* client) {
readState(client);
- if (_state.getState() != StorageStateEnum::enabled) {
+ if (_state->getState() != StorageStateEnum::enabled) {
// If we are recently disabled, then stop sending metrics
return;
}
// Build outbound request
FreeMonMetricsRequest req;
- invariant(!_state.getRegistrationId().empty());
+ invariant(!_state->getRegistrationId().empty());
req.setVersion(kProtocolVersion);
req.setEncoding(MetricsEncodingEnum::snappy);
- req.setId(_state.getRegistrationId());
+ req.setId(_state->getRegistrationId());
// Get the buffered metrics
auto metrics = compressMetrics(_metricsBuffer);
req.setMetrics(ConstDataRange(metrics.data(), metrics.size()));
+ _lastMetricsSend = Date_t::now();
+
// Send the async request
doAsyncCallback<FreeMonMetricsResponse>(
this,
@@ -770,7 +779,7 @@ void FreeMonProcessor::doAsyncMetricsComplete(
warning() << "Free Monitoring metrics uploading halted due to " << s;
// Disable free monitoring on validation errors
- _state.setState(StorageStateEnum::disabled);
+ _state->setState(StorageStateEnum::disabled);
writeState(client);
// If validation fails, we do not retry
@@ -790,49 +799,81 @@ void FreeMonProcessor::doAsyncMetricsComplete(
_metricsBuffer.reset();
- if (resp.getId().is_initialized()) {
- _state.setRegistrationId(resp.getId().get());
- }
+ {
+ auto state = _state.synchronize();
- if (resp.getUserReminder().is_initialized()) {
- _state.setUserReminder(resp.getUserReminder().get());
- }
+ if (resp.getId().is_initialized()) {
+ state->setRegistrationId(resp.getId().get());
+ }
- if (resp.getInformationalURL().is_initialized()) {
- _state.setInformationalURL(resp.getInformationalURL().get());
- }
+ if (resp.getUserReminder().is_initialized()) {
+ state->setUserReminder(resp.getUserReminder().get());
+ }
+
+ if (resp.getInformationalURL().is_initialized()) {
+ state->setInformationalURL(resp.getInformationalURL().get());
+ }
- if (resp.getMessage().is_initialized()) {
- _state.setMessage(resp.getMessage().get());
+ if (resp.getMessage().is_initialized()) {
+ state->setMessage(resp.getMessage().get());
+ }
}
// Persist state
writeState(client);
// Reset retry counter
- _metricsRetry.setMin(Seconds(resp.getReportingInterval()));
- _metricsRetry.reset();
+ _metricsRetry->setMin(Seconds(resp.getReportingInterval()));
+ _metricsRetry->reset();
// Enqueue next metrics upload
enqueue(FreeMonMessage::createWithDeadline(FreeMonMessageType::MetricsSend,
- _registrationRetry.getNextDeadline(client)));
+ _registrationRetry->getNextDeadline(client)));
}
void FreeMonProcessor::doAsyncMetricsFail(
Client* client, const FreeMonMessageWithPayload<FreeMonMessageType::AsyncMetricsFail>* msg) {
- if (!_metricsRetry.incrementError()) {
+ if (!_metricsRetry->incrementError()) {
// We have exceeded our retry
warning() << "Free Monitoring is abandoning metrics upload after excess retries";
return;
}
LOG(1) << "Free Monitoring Metrics upload failed with status " << msg->getPayload()
- << ", retrying in " << _metricsRetry.getNextDuration();
+ << ", retrying in " << _metricsRetry->getNextDuration();
// Enqueue next metrics upload
enqueue(FreeMonMessage::createWithDeadline(FreeMonMessageType::MetricsSend,
- _metricsRetry.getNextDeadline(client)));
+ _metricsRetry->getNextDeadline(client)));
+}
+
+void FreeMonProcessor::getServerStatus(OperationContext* opCtx, BSONObjBuilder* status) {
+ try {
+ readState(opCtx);
+ } catch (const DBException&) {
+ // readState() may throw if invoked during shutdown (as in ReplSetTest cleanup).
+ // If we have a lastReadState, go ahead and use that, otherwise just give up already.
+ if (!_lastReadState.get()) {
+ return;
+ }
+ }
+
+ if (!_lastReadState.get()) {
+ // _state gets initialized by readState() regardless,
+ // use _lastReadState to differential "undecided" from default.
+ status->append("state", "undecided");
+ return;
+ }
+
+ status->append("state", StorageState_serializer(_state->getState()));
+ status->append("retryIntervalSecs", durationCount<Seconds>(_metricsRetry->getNextDuration()));
+ auto lastMetricsSend = _lastMetricsSend.get();
+ if (lastMetricsSend) {
+ status->append("lastRunTime", lastMetricsSend->toString());
+ }
+ status->append("registerErrors", static_cast<long long>(_registrationRetry->getCount()));
+ status->append("metricsErrors", static_cast<long long>(_metricsRetry->getCount()));
}
void FreeMonProcessor::doOnTransitionToPrimary(Client* client) {
@@ -871,7 +912,7 @@ void FreeMonProcessor::doNotifyOnUpsert(
<< newState.getVersion(),
newState.getVersion() == kStorageVersion);
- processInMemoryStateChange(_state, newState);
+ processInMemoryStateChange(_state.get(), newState);
// Note: enabled -> disabled is handled implicitly by register and send metrics checks
// after _state is updated below
@@ -896,7 +937,7 @@ void FreeMonProcessor::doNotifyOnDelete(Client* client) {
// the same and stop free monitoring. We continue collecting though.
// So we mark the internal state as disabled which stop registration and metrics send
- _state.setState(StorageStateEnum::disabled);
+ _state->setState(StorageStateEnum::disabled);
}
void FreeMonProcessor::doNotifyOnRollback(Client* client) {
@@ -904,12 +945,12 @@ void FreeMonProcessor::doNotifyOnRollback(Client* client) {
// We should re-read the disk state and proceed.
// copy the in-memory state
- auto originalState = _state;
+ auto originalState = _state.get();
// Re-read state from disk
readState(client);
- auto& newState = _state;
+ auto newState = _state.get();
if (newState != originalState) {
processInMemoryStateChange(originalState, newState);
diff --git a/src/mongo/db/free_mon/free_mon_processor.h b/src/mongo/db/free_mon/free_mon_processor.h
index 64734d3a981..846c7db1d55 100644
--- a/src/mongo/db/free_mon/free_mon_processor.h
+++ b/src/mongo/db/free_mon/free_mon_processor.h
@@ -28,6 +28,7 @@
#pragma once
#include <boost/optional.hpp>
+#include <boost/thread/synchronized_value.hpp>
#include <cstdint>
#include <deque>
#include <memory>
@@ -84,7 +85,7 @@ public:
/**
* Get the next retry duration.
*/
- Seconds getNextDuration() {
+ Seconds getNextDuration() const {
dassert(_current != Seconds(0));
return _current;
}
@@ -92,7 +93,7 @@ public:
/**
* Get the next retry deadline
*/
- Date_t getNextDeadline(Client* client) {
+ Date_t getNextDeadline(Client* client) const {
return client->getServiceContext()->getPreciseClockSource()->now() + _current;
}
@@ -118,6 +119,10 @@ public:
bool incrementError() final;
+ size_t getCount() const {
+ return _retryCount;
+ }
+
private:
// Random number generator for jitter
PseudoRandom& _random;
@@ -154,6 +159,10 @@ public:
bool incrementError() final;
+ size_t getCount() const {
+ return _retryCount;
+ }
+
private:
// Random number generator for jitter
PseudoRandom& _random;
@@ -308,6 +317,11 @@ private:
/**
* Read the state from the database.
*/
+ void readState(OperationContext* opCtx);
+
+ /**
+ * Create a short-lived opCtx and read the state from the database.
+ */
void readState(Client* client);
/**
@@ -403,6 +417,14 @@ private:
void processInMemoryStateChange(const FreeMonStorageState& originalState,
const FreeMonStorageState& newState);
+protected:
+ friend class FreeMonController;
+
+ /**
+ * Server status section with state for active processor.
+ */
+ void getServerStatus(OperationContext* opCtx, BSONObjBuilder* status);
+
private:
// Collection of collectors to send on registration
FreeMonCollectorCollection& _registration;
@@ -417,10 +439,10 @@ private:
PseudoRandom _random;
// Registration Retry logic
- RegistrationRetryCounter _registrationRetry;
+ boost::synchronized_value<RegistrationRetryCounter> _registrationRetry;
// Metrics Retry logic
- MetricsRetryCounter _metricsRetry;
+ boost::synchronized_value<MetricsRetryCounter> _metricsRetry;
// Interval for gathering metrics
Seconds _metricsGatherInterval;
@@ -428,6 +450,9 @@ private:
// Buffer of metrics to upload
MetricsBuffer _metricsBuffer;
+ // When did we last send a metrics batch?
+ boost::synchronized_value<boost::optional<Date_t>> _lastMetricsSend;
+
// List of tags from server configuration registration
std::vector<std::string> _tags;
@@ -438,13 +463,13 @@ private:
std::vector<std::shared_ptr<FreeMonMessage>> _pendingRegisters;
// Last read storage state
- boost::optional<FreeMonStorageState> _lastReadState;
+ boost::synchronized_value<boost::optional<FreeMonStorageState>> _lastReadState;
// When we change to primary, do we register?
bool _registerOnTransitionToPrimary{false};
// Pending update to disk
- FreeMonStorageState _state;
+ boost::synchronized_value<FreeMonStorageState> _state;
// Countdown launch to support manual cranking
FreeMonCountdownLatch _countdown;
diff --git a/src/mongo/db/free_mon/free_mon_status.cpp b/src/mongo/db/free_mon/free_mon_status.cpp
new file mode 100644
index 00000000000..73ff7165052
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_status.cpp
@@ -0,0 +1,64 @@
+/**
+ * Copyright (C) 2018 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.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/commands/server_status.h"
+#include "mongo/db/free_mon/free_mon_controller.h"
+
+namespace mongo {
+namespace {
+
+class FreeMonServerStatus : public ServerStatusSection {
+public:
+ FreeMonServerStatus() : ServerStatusSection("freeMonitoring") {}
+
+ bool includeByDefault() const final {
+ return true;
+ }
+
+ void addRequiredPrivileges(std::vector<Privilege>* out) final {
+ out->push_back(Privilege(ResourcePattern::forClusterResource(),
+ ActionType::checkFreeMonitoringStatus));
+ }
+
+ BSONObj generateSection(OperationContext* opCtx, const BSONElement& configElement) const final {
+ auto* controller = FreeMonController::get(opCtx->getServiceContext());
+ if (!controller) {
+ return BSON("state"
+ << "disabled");
+ }
+
+ BSONObjBuilder builder;
+ controller->getServerStatus(opCtx, &builder);
+ return builder.obj();
+ }
+} freeMonServerStatus;
+
+} // namespace
+} // namespace mongo