summaryrefslogtreecommitdiff
path: root/src/mongo/db/free_mon/free_mon_mongod.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/free_mon/free_mon_mongod.cpp')
-rw-r--r--src/mongo/db/free_mon/free_mon_mongod.cpp373
1 files changed, 373 insertions, 0 deletions
diff --git a/src/mongo/db/free_mon/free_mon_mongod.cpp b/src/mongo/db/free_mon/free_mon_mongod.cpp
new file mode 100644
index 00000000000..eda9f8487ee
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_mongod.cpp
@@ -0,0 +1,373 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kControl
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/free_mon/free_mon_mongod.h"
+
+#include <mutex>
+#include <snappy.h>
+#include <string>
+
+#include "mongo/base/data_type_validated.h"
+#include "mongo/base/error_codes.h"
+#include "mongo/base/status.h"
+#include "mongo/bson/bsonelement.h"
+#include "mongo/bson/bsonmisc.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/bson/bsontypes.h"
+#include "mongo/db/db_raii.h"
+#include "mongo/db/free_mon/free_mon_controller.h"
+#include "mongo/db/free_mon/free_mon_message.h"
+#include "mongo/db/free_mon/free_mon_mongod_gen.h"
+#include "mongo/db/free_mon/free_mon_network.h"
+#include "mongo/db/free_mon/free_mon_op_observer.h"
+#include "mongo/db/free_mon/free_mon_options.h"
+#include "mongo/db/free_mon/free_mon_protocol_gen.h"
+#include "mongo/db/free_mon/free_mon_storage.h"
+#include "mongo/db/ftdc/ftdc_server.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/repl/replication_coordinator.h"
+#include "mongo/db/service_context.h"
+#include "mongo/executor/network_interface_factory.h"
+#include "mongo/executor/thread_pool_task_executor.h"
+#include "mongo/rpc/object_check.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/concurrency/thread_pool.h"
+#include "mongo/util/future.h"
+#include "mongo/util/net/http_client.h"
+#include "mongo/util/testing_proctor.h"
+
+namespace mongo {
+
+namespace {
+
+constexpr Seconds kDefaultMetricsGatherInterval(60);
+
+auto makeTaskExecutor(ServiceContext* /*serviceContext*/) {
+ ThreadPool::Options tpOptions;
+ tpOptions.poolName = "FreeMonHTTP";
+ tpOptions.maxThreads = 2;
+ tpOptions.onCreateThread = [](const std::string& threadName) {
+ Client::initThread(threadName.c_str());
+ };
+ return std::make_unique<executor::ThreadPoolTaskExecutor>(
+ std::make_unique<ThreadPool>(tpOptions), executor::makeNetworkInterface("FreeMonNet"));
+}
+
+class FreeMonNetworkHttp final : public FreeMonNetworkInterface {
+public:
+ explicit FreeMonNetworkHttp(ServiceContext* serviceContext) {
+ _executor = makeTaskExecutor(serviceContext);
+ _executor->startup();
+ _client = HttpClient::create();
+ _client->allowInsecureHTTP(TestingProctor::instance().isEnabled());
+ _client->setHeaders({"Content-Type: application/octet-stream",
+ "Accept: application/octet-stream",
+ "Expect:"});
+ }
+
+ Future<FreeMonRegistrationResponse> sendRegistrationAsync(
+ const FreeMonRegistrationRequest& req) override {
+ BSONObj reqObj = req.toBSON();
+ auto data = std::make_shared<std::vector<std::uint8_t>>(
+ reqObj.objdata(), reqObj.objdata() + reqObj.objsize());
+
+ return post("/register", data).then([](DataBuilder&& blob) {
+ if (!blob.size()) {
+ uasserted(ErrorCodes::FreeMonHttpTemporaryFailure, "Empty response received");
+ }
+
+ auto blobSize = blob.size();
+ auto blobData = blob.release();
+ ConstDataRange cdr(blobData.get(), blobSize);
+ BSONObj respObj = cdr.read<Validated<BSONObj>>();
+
+ auto resp =
+ FreeMonRegistrationResponse::parse(IDLParserErrorContext("response"), respObj);
+
+ return resp;
+ });
+ }
+
+ Future<FreeMonMetricsResponse> sendMetricsAsync(const FreeMonMetricsRequest& req) override {
+ BSONObj reqObj = req.toBSON();
+ auto data = std::make_shared<std::vector<std::uint8_t>>(
+ reqObj.objdata(), reqObj.objdata() + reqObj.objsize());
+
+ return post("/metrics", data).then([](DataBuilder&& blob) {
+ if (!blob.size()) {
+ uasserted(ErrorCodes::FreeMonHttpTemporaryFailure, "Empty response received");
+ }
+
+ auto blobSize = blob.size();
+ auto blobData = blob.release();
+ ConstDataRange cdr(blobData.get(), blobSize);
+
+ BSONObj respObj = cdr.read<Validated<BSONObj>>();
+
+ auto resp = FreeMonMetricsResponse::parse(IDLParserErrorContext("response"), respObj);
+
+ return resp;
+ });
+ }
+
+private:
+ Future<DataBuilder> post(StringData path,
+ std::shared_ptr<std::vector<std::uint8_t>> data) const {
+ auto pf = makePromiseFuture<DataBuilder>();
+ std::string url(FreeMonEndpointURL + path.toString());
+
+ auto status = _executor->scheduleWork(
+ [promise = std::move(pf.promise), url = std::move(url), data = std::move(data), this](
+ const executor::TaskExecutor::CallbackArgs& cbArgs) mutable {
+ ConstDataRange cdr(data->data(), data->size());
+ try {
+ auto result = this->_client->post(url, cdr);
+ promise.emplaceValue(std::move(result));
+ } catch (...) {
+ promise.setError(exceptionToStatus());
+ }
+ });
+
+ uassertStatusOK(status);
+ return std::move(pf.future);
+ }
+
+private:
+ std::unique_ptr<HttpClient> _client;
+ std::unique_ptr<executor::ThreadPoolTaskExecutor> _executor;
+};
+
+/**
+ * Collect the mms-automation state document from local.clustermanager during registration.
+ */
+class FreeMonLocalClusterManagerCollector : public FreeMonCollectorInterface {
+public:
+ std::string name() const final {
+ return "clustermanager";
+ }
+
+ void collect(OperationContext* opCtx, BSONObjBuilder& builder) {
+ auto optionalObj = FreeMonStorage::readClusterManagerState(opCtx);
+ if (optionalObj.is_initialized()) {
+ builder.appendElements(optionalObj.get());
+ }
+ }
+};
+
+/**
+ * Get the "storageEngine" section of "serverStatus" during registration.
+ */
+class FreeMonLocalStorageEngineStatusCollector : public FTDCSimpleInternalCommandCollector {
+public:
+ FreeMonLocalStorageEngineStatusCollector()
+ : FTDCSimpleInternalCommandCollector(
+ "serverStatus",
+ "serverStatus",
+ "",
+ // Try to filter server status to make it cheaper to collect. Harmless if we gather
+ // extra
+ BSON("serverStatus" << 1 << "storageEngine" << true << "extra_info" << false
+ << "opLatencies" << false << "opcountersRepl" << false
+ << "opcounters" << false << "transactions" << false
+ << "connections" << false << "network" << false << "tcMalloc"
+ << false << "network" << false << "wiredTiger" << false
+ << "sharding" << false << "metrics" << false)) {}
+
+ std::string name() const final {
+ return "storageEngine";
+ }
+
+ void collect(OperationContext* opCtx, BSONObjBuilder& builder) {
+ BSONObjBuilder localBuilder;
+
+ FTDCSimpleInternalCommandCollector::collect(opCtx, localBuilder);
+
+ BSONObj obj = localBuilder.obj();
+
+ builder.appendElements(obj["storageEngine"].Obj());
+ }
+};
+
+/**
+ * Collect the UUIDs associated with the named collections (if available).
+ */
+class FreeMonNamespaceUUIDCollector : public FreeMonCollectorInterface {
+public:
+ FreeMonNamespaceUUIDCollector(std::set<NamespaceString> namespaces)
+ : _namespaces(std::move(namespaces)) {}
+
+ std::string name() const final {
+ return "uuid";
+ }
+
+ void collect(OperationContext* opCtx, BSONObjBuilder& builder) {
+ auto catalog = CollectionCatalog::get(opCtx);
+ for (auto& nss : _namespaces) {
+ auto optUUID = catalog->lookupUUIDByNSS(opCtx, nss);
+ if (optUUID) {
+ builder << nss.toString() << optUUID.get();
+ }
+ }
+ }
+
+private:
+ std::set<NamespaceString> _namespaces;
+};
+
+} // namespace
+
+Status onValidateFreeMonEndpointURL(StringData str) {
+ // Check for http, not https here because testEnabled may not be set yet
+ if (!str.startsWith("http"_sd) != 0) {
+ return Status(ErrorCodes::BadValue,
+ "cloudFreeMonitoringEndpointURL only supports http:// URLs");
+ }
+
+ return Status::OK();
+}
+
+void registerCollectors(FreeMonController* controller) {
+ // These are collected only at registration
+ //
+ // CmdBuildInfo
+ controller->addRegistrationCollector(std::make_unique<FTDCSimpleInternalCommandCollector>(
+ "buildInfo", "buildInfo", "", BSON("buildInfo" << 1)));
+
+ // HostInfoCmd
+ controller->addRegistrationCollector(std::make_unique<FTDCSimpleInternalCommandCollector>(
+ "hostInfo", "hostInfo", "", BSON("hostInfo" << 1)));
+
+ // Add storageEngine section from serverStatus
+ controller->addRegistrationCollector(
+ std::make_unique<FreeMonLocalStorageEngineStatusCollector>());
+
+ // Gather one document from local.clustermanager
+ controller->addRegistrationCollector(std::make_unique<FreeMonLocalClusterManagerCollector>());
+
+ // These are periodically for metrics upload
+ //
+ controller->addMetricsCollector(std::make_unique<FTDCSimpleInternalCommandCollector>(
+ "getDiagnosticData", "diagnosticData", "", BSON("getDiagnosticData" << 1)));
+
+ // These are collected at registration and as metrics periodically
+ //
+ if (repl::ReplicationCoordinator::get(getGlobalServiceContext())->getReplicationMode() !=
+ repl::ReplicationCoordinator::modeNone) {
+ // CmdReplSetGetConfig
+ controller->addRegistrationCollector(std::make_unique<FTDCSimpleInternalCommandCollector>(
+ "replSetGetConfig", "replSetGetConfig", "", BSON("replSetGetConfig" << 1)));
+
+ controller->addMetricsCollector(std::make_unique<FTDCSimpleInternalCommandCollector>(
+ "replSetGetConfig", "replSetGetConfig", "", BSON("replSetGetConfig" << 1)));
+
+ // Collect UUID for certain collections.
+ std::set<NamespaceString> namespaces({NamespaceString("local.oplog.rs")});
+ controller->addRegistrationCollector(
+ std::make_unique<FreeMonNamespaceUUIDCollector>(namespaces));
+ controller->addMetricsCollector(
+ std::make_unique<FreeMonNamespaceUUIDCollector>(namespaces));
+ }
+
+ controller->addRegistrationCollector(std::make_unique<FTDCSimpleInternalCommandCollector>(
+ "isMaster", "isMaster", "", BSON("isMaster" << 1)));
+
+ controller->addMetricsCollector(std::make_unique<FTDCSimpleInternalCommandCollector>(
+ "isMaster", "isMaster", "", BSON("isMaster" << 1)));
+}
+
+void startFreeMonitoring(ServiceContext* serviceContext) {
+ if (globalFreeMonParams.freeMonitoringState == EnableCloudStateEnum::kOff) {
+ return;
+ }
+
+ if (!TestingProctor::instance().isEnabled()) {
+ uassert(50774,
+ "ExportedFreeMonEndpointURL only supports https:// URLs",
+ FreeMonEndpointURL.compare(0, 5, "https") == 0);
+ }
+
+ auto network = std::unique_ptr<FreeMonNetworkInterface>(new FreeMonNetworkHttp(serviceContext));
+
+ auto controller = std::make_unique<FreeMonController>(std::move(network));
+
+ auto controllerPtr = controller.get();
+
+ registerCollectors(controller.get());
+
+ // Install the new controller
+ FreeMonController::init(getGlobalServiceContext(), std::move(controller));
+
+ RegistrationType registrationType = RegistrationType::DoNotRegister;
+ if (globalFreeMonParams.freeMonitoringState == EnableCloudStateEnum::kOn) {
+ // If replication is enabled, we may need to register on becoming primary
+ if (repl::ReplicationCoordinator::get(getGlobalServiceContext())->getReplicationMode() !=
+ repl::ReplicationCoordinator::modeNone) {
+ registrationType = RegistrationType::RegisterAfterOnTransitionToPrimary;
+ } else {
+ registrationType = RegistrationType::RegisterOnStart;
+ }
+ } else if (globalFreeMonParams.freeMonitoringState == EnableCloudStateEnum::kRuntime) {
+ registrationType = RegistrationType::RegisterAfterOnTransitionToPrimaryIfEnabled;
+ }
+
+ controllerPtr->start(registrationType,
+ globalFreeMonParams.freeMonitoringTags,
+ Seconds(kDefaultMetricsGatherInterval));
+}
+
+void stopFreeMonitoring() {
+ if (globalFreeMonParams.freeMonitoringState == EnableCloudStateEnum::kOff) {
+ return;
+ }
+
+ auto controller = FreeMonController::get(getGlobalServiceContext());
+
+ if (controller != nullptr) {
+ controller->stop();
+ }
+}
+
+void notifyFreeMonitoringOnTransitionToPrimary() {
+ auto controller = FreeMonController::get(getGlobalServiceContext());
+
+ if (controller != nullptr) {
+ controller->notifyOnTransitionToPrimary();
+ }
+}
+
+void setupFreeMonitoringOpObserver(OpObserverRegistry* registry) {
+ registry->addObserver(std::make_unique<FreeMonOpObserver>());
+}
+
+} // namespace mongo