/** * 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 * . * * 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_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl #include "mongo/platform/basic.h" #include "mongo/db/free_mon/free_mon_mongod.h" #include #include #include #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/commands/test_commands_enabled.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/log.h" #include "mongo/util/net/http_client.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 stdx::make_unique( std::make_unique(tpOptions), executor::makeNetworkInterface("FreeMonNet")); } class FreeMonNetworkHttp : public FreeMonNetworkInterface { public: explicit FreeMonNetworkHttp(ServiceContext* serviceContext) { _executor = makeTaskExecutor(serviceContext); _executor->startup(); _client = HttpClient::create(); _client->allowInsecureHTTP(getTestCommandsEnabled()); _client->setHeaders({"Content-Type: application/octet-stream", "Accept: application/octet-stream", "Expect:"}); } ~FreeMonNetworkHttp() final = default; Future sendRegistrationAsync( const FreeMonRegistrationRequest& req) override { BSONObj reqObj = req.toBSON(); auto data = std::make_shared>( 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>(); auto resp = FreeMonRegistrationResponse::parse(IDLParserErrorContext("response"), respObj); return resp; }); } Future sendMetricsAsync(const FreeMonMetricsRequest& req) override { BSONObj reqObj = req.toBSON(); auto data = std::make_shared>( 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>(); auto resp = FreeMonMetricsResponse::parse(IDLParserErrorContext("response"), respObj); return resp; }); } private: Future post(StringData path, std::shared_ptr> data) const { auto pf = makePromiseFuture(); 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 _client; std::unique_ptr _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 namespaces) : _namespaces(std::move(namespaces)) {} std::string name() const final { return "uuid"; } void collect(OperationContext* opCtx, BSONObjBuilder& builder) { for (auto nss : _namespaces) { AutoGetCollectionForRead coll(opCtx, nss); auto* collection = coll.getCollection(); if (collection) { auto optUUID = collection->uuid(); if (optUUID) { builder << nss.toString() << optUUID.get(); } } } } private: std::set _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(stdx::make_unique( "buildInfo", "buildInfo", "", BSON("buildInfo" << 1))); // HostInfoCmd controller->addRegistrationCollector(stdx::make_unique( "hostInfo", "hostInfo", "", BSON("hostInfo" << 1))); // Add storageEngine section from serverStatus controller->addRegistrationCollector( stdx::make_unique()); // Gather one document from local.clustermanager controller->addRegistrationCollector(stdx::make_unique()); // These are periodically for metrics upload // controller->addMetricsCollector(stdx::make_unique( "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(stdx::make_unique( "replSetGetConfig", "replSetGetConfig", "", BSON("replSetGetConfig" << 1))); controller->addMetricsCollector(stdx::make_unique( "replSetGetConfig", "replSetGetConfig", "", BSON("replSetGetConfig" << 1))); // Collect UUID for certain collections. std::set namespaces({NamespaceString("local.oplog.rs")}); controller->addRegistrationCollector( std::make_unique(namespaces)); controller->addMetricsCollector( std::make_unique(namespaces)); } controller->addRegistrationCollector(stdx::make_unique( "isMaster", "isMaster", "", BSON("isMaster" << 1))); controller->addMetricsCollector(stdx::make_unique( "isMaster", "isMaster", "", BSON("isMaster" << 1))); } void startFreeMonitoring(ServiceContext* serviceContext) { if (globalFreeMonParams.freeMonitoringState == EnableCloudStateEnum::kOff) { return; } // Check for http, not https here because testEnabled may not be set yet if (!getTestCommandsEnabled()) { uassert(50774, "ExportedFreeMonEndpointURL only supports https:// URLs", FreeMonEndpointURL.compare(0, 5, "https") == 0); } auto network = std::unique_ptr(new FreeMonNetworkHttp(serviceContext)); auto controller = stdx::make_unique(std::move(network)); auto controllerPtr = controller.get(); registerCollectors(controller.get()); // Install the new controller FreeMonController::set(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(stdx::make_unique()); } } // namespace mongo