diff options
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/client/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/client/async_client.cpp | 3 | ||||
-rw-r--r-- | src/mongo/client/authenticate.cpp | 17 | ||||
-rw-r--r-- | src/mongo/client/authenticate.h | 4 | ||||
-rw-r--r-- | src/mongo/client/dbclient_base.cpp | 11 | ||||
-rw-r--r-- | src/mongo/client/native_sasl_client_session.cpp | 33 | ||||
-rw-r--r-- | src/mongo/client/sasl_client_authenticate_impl.cpp | 4 | ||||
-rw-r--r-- | src/mongo/client/scram_client_cache.h | 23 | ||||
-rw-r--r-- | src/mongo/db/commands/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/commands/server_status.cpp | 268 | ||||
-rw-r--r-- | src/mongo/db/commands/server_status.h | 24 | ||||
-rw-r--r-- | src/mongo/db/commands/server_status_command.cpp | 270 | ||||
-rw-r--r-- | src/mongo/db/repl/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/executor/connection_pool_tl.cpp | 4 | ||||
-rw-r--r-- | src/mongo/executor/network_connection_hook.h | 2 |
15 files changed, 402 insertions, 267 deletions
diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript index 27d48509935..0f8a4903bfa 100644 --- a/src/mongo/client/SConscript +++ b/src/mongo/client/SConscript @@ -128,6 +128,7 @@ saslClientEnv.Library( "sasl_aws_client" if get_option('ssl') == 'on' else '', ], LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/commands/server_status_core', '$BUILD_DIR/mongo/util/net/http_client', ], SYSLIBDEPS=saslLibs, diff --git a/src/mongo/client/async_client.cpp b/src/mongo/client/async_client.cpp index 6b3085efd4d..3fcd468d8d5 100644 --- a/src/mongo/client/async_client.cpp +++ b/src/mongo/client/async_client.cpp @@ -98,7 +98,7 @@ BSONObj AsyncDBClient::_buildIsMasterRequest(const std::string& appName, } if (hook) { - return hook->augmentIsMasterRequest(bob.obj()); + return hook->augmentIsMasterRequest(remote(), bob.obj()); } else { return bob.obj(); } @@ -192,6 +192,7 @@ Future<void> AsyncDBClient::authenticateInternal( #endif return auth::authenticateInternalClient(clientName, + remote(), mechanismHint, auth::StepDownBehavior::kKillConnection, _makeAuthRunCommandHook(), diff --git a/src/mongo/client/authenticate.cpp b/src/mongo/client/authenticate.cpp index af3b48f73b1..7180b707f10 100644 --- a/src/mongo/client/authenticate.cpp +++ b/src/mongo/client/authenticate.cpp @@ -248,27 +248,28 @@ Future<std::string> negotiateSaslMechanism(RunCommandHook runCommand, Future<void> authenticateInternalClient( const std::string& clientSubjectName, + const HostAndPort& remote, boost::optional<std::string> mechanismHint, StepDownBehavior stepDownBehavior, RunCommandHook runCommand, std::shared_ptr<InternalAuthParametersProvider> internalParamsProvider) { return negotiateSaslMechanism( runCommand, internalSecurity.user->getName(), mechanismHint, stepDownBehavior) - .then([runCommand, clientSubjectName, internalParamsProvider]( + .then([runCommand, clientSubjectName, remote, internalParamsProvider]( std::string mechanism) -> Future<void> { auto params = internalParamsProvider->get(0, mechanism); if (params.isEmpty()) { return Status(ErrorCodes::BadValue, "Missing authentication parameters for internal user auth"); } - return authenticateClient(params, HostAndPort(), clientSubjectName, runCommand) + return authenticateClient(params, remote, clientSubjectName, runCommand) .onError<ErrorCodes::AuthenticationFailed>( - [runCommand, clientSubjectName, mechanism, internalParamsProvider]( + [runCommand, clientSubjectName, remote, mechanism, internalParamsProvider]( Status status) -> Future<void> { auto altCreds = internalParamsProvider->get(1, mechanism); if (!altCreds.isEmpty()) { return authenticateClient( - altCreds, HostAndPort(), clientSubjectName, runCommand); + altCreds, remote, clientSubjectName, runCommand); } return status; }); @@ -391,7 +392,9 @@ SpeculativeAuthType speculateAuth(BSONObjBuilder* isMasterRequest, } SpeculativeAuthType speculateInternalAuth( - BSONObjBuilder* isMasterRequest, std::shared_ptr<SaslClientSession>* saslClientSession) try { + const HostAndPort& remoteHost, + BSONObjBuilder* isMasterRequest, + std::shared_ptr<SaslClientSession>* saslClientSession) try { auto params = getInternalAuthParams(0, kMechanismScramSha256.toString()); if (params.isEmpty()) { return SpeculativeAuthType::kNone; @@ -400,8 +403,8 @@ SpeculativeAuthType speculateInternalAuth( auto mechanism = getBSONString(params, saslCommandMechanismFieldName); auto authDB = getBSONString(params, saslCommandUserDBFieldName); - auto ret = _speculateAuth( - isMasterRequest, mechanism, HostAndPort(), authDB, params, saslClientSession); + auto ret = + _speculateAuth(isMasterRequest, mechanism, remoteHost, authDB, params, saslClientSession); if (!ret.isOK()) { return SpeculativeAuthType::kNone; } diff --git a/src/mongo/client/authenticate.h b/src/mongo/client/authenticate.h index 1a5fe609665..b5b4876c573 100644 --- a/src/mongo/client/authenticate.h +++ b/src/mongo/client/authenticate.h @@ -147,6 +147,7 @@ Future<void> authenticateClient(const BSONObj& params, */ Future<void> authenticateInternalClient( const std::string& clientSubjectName, + const HostAndPort& remote, boost::optional<std::string> mechanismHint, StepDownBehavior stepDownBehavior, RunCommandHook runCommand, @@ -205,7 +206,8 @@ SpeculativeAuthType speculateAuth(BSONObjBuilder* isMasterRequest, * Constructs a "speculativeAuthenticate" or "speculativeSaslStart" * payload for an isMaster request using internal (intracluster) authentication. */ -SpeculativeAuthType speculateInternalAuth(BSONObjBuilder* isMasterRequest, +SpeculativeAuthType speculateInternalAuth(const HostAndPort& remoteHost, + BSONObjBuilder* isMasterRequest, std::shared_ptr<SaslClientSession>* saslClientSession); } // namespace auth diff --git a/src/mongo/client/dbclient_base.cpp b/src/mongo/client/dbclient_base.cpp index dce2f0a5b2a..2eecc7ae6ec 100644 --- a/src/mongo/client/dbclient_base.cpp +++ b/src/mongo/client/dbclient_base.cpp @@ -428,10 +428,13 @@ Status DBClientBase::authenticateInternalUser(auth::StepDownBehavior stepDownBeh #endif auto authProvider = auth::createDefaultInternalAuthProvider(); - auto status = - auth::authenticateInternalClient( - clientName, boost::none, stepDownBehavior, _makeAuthRunCommandHook(), authProvider) - .getNoThrow(); + auto status = auth::authenticateInternalClient(clientName, + HostAndPort(getServerAddress()), + boost::none, + stepDownBehavior, + _makeAuthRunCommandHook(), + authProvider) + .getNoThrow(); if (status.isOK()) { return status; } diff --git a/src/mongo/client/native_sasl_client_session.cpp b/src/mongo/client/native_sasl_client_session.cpp index 3d37f9db9cc..146c344e42e 100644 --- a/src/mongo/client/native_sasl_client_session.cpp +++ b/src/mongo/client/native_sasl_client_session.cpp @@ -32,6 +32,7 @@ #include "mongo/client/native_sasl_client_session.h" #include "mongo/base/init.h" +#include "mongo/bson/bsonobjbuilder.h" #include "mongo/client/sasl_client_conversation.h" #include "mongo/client/sasl_plain_client_conversation.h" #include "mongo/client/sasl_scram_client_conversation.h" @@ -39,6 +40,7 @@ #include "mongo/config.h" #include "mongo/crypto/sha1_block.h" #include "mongo/crypto/sha256_block.h" +#include "mongo/db/commands/server_status.h" #include "mongo/util/str.h" #ifdef MONGO_CONFIG_SSL @@ -60,6 +62,37 @@ MONGO_INITIALIZER(NativeSaslClientContext)(InitializerContext* context) { auto* scramsha1ClientCache = new SCRAMClientCache<SHA1Block>; auto* scramsha256ClientCache = new SCRAMClientCache<SHA256Block>; +template <typename HashBlock> +void cacheToBSON(SCRAMClientCache<HashBlock>* cache, StringData name, BSONObjBuilder* builder) { + auto stats = cache->getStats(); + + BSONObjBuilder sub(builder->subobjStart(name)); + sub.append("count", stats.count); + sub.append("hits", stats.hits); + sub.append("misses", stats.misses); +} + +/** + * Output stats about the SCRAM client cache to server status. + */ +class ScramCacheStatsStatusSection : ServerStatusSection { +public: + ScramCacheStatsStatusSection() : ServerStatusSection("scramCache") {} + + bool includeByDefault() const override { + return true; + } + + BSONObj generateSection(OperationContext* opCtx, + const BSONElement& configElement) const override { + BSONObjBuilder builder; + cacheToBSON(scramsha1ClientCache, "SCRAM-SHA-1", &builder); + cacheToBSON(scramsha256ClientCache, "SCRAM-SHA-256", &builder); + return builder.obj(); + }; + +} scramCacheStatusSection; + } // namespace NativeSaslClientSession::NativeSaslClientSession() diff --git a/src/mongo/client/sasl_client_authenticate_impl.cpp b/src/mongo/client/sasl_client_authenticate_impl.cpp index 323f7a6b321..3c9bd422270 100644 --- a/src/mongo/client/sasl_client_authenticate_impl.cpp +++ b/src/mongo/client/sasl_client_authenticate_impl.cpp @@ -117,6 +117,10 @@ Status saslConfigureSession(SaslClientSession* session, const HostAndPort& hostname, StringData targetDatabase, const BSONObj& saslParameters) { + // SERVER-59876 Ensure hostname is never empty. If it is empty, the client-side SCRAM cache will + // not be used which creates performance problems. + dassert(!hostname.empty()); + std::string mechanism; Status status = bsonExtractStringField(saslParameters, saslCommandMechanismFieldName, &mechanism); diff --git a/src/mongo/client/scram_client_cache.h b/src/mongo/client/scram_client_cache.h index fb43d76b622..d8cc4493cb8 100644 --- a/src/mongo/client/scram_client_cache.h +++ b/src/mongo/client/scram_client_cache.h @@ -68,6 +68,15 @@ private: using HostToSecretsMap = stdx::unordered_map<HostAndPort, HostToSecretsPair>; public: + struct Stats { + // Count of cache entries + int64_t count{0}; + // Number of cache hits + int64_t hits{0}; + // Number of cache misses + int64_t misses{0}; + }; + /** * Returns precomputed SCRAMSecrets, if one has already been * stored for the specified hostname and the provided presecrets @@ -81,6 +90,7 @@ public: // Search the cache for a record associated with the host we're trying to connect to. auto foundSecret = _hostToSecrets.find(target); if (foundSecret == _hostToSecrets.end()) { + ++_stats.misses; return {}; } @@ -89,8 +99,10 @@ public: // stale cached secrets. We'll need to rerun the SCRAM computation. const auto& foundPresecrets = foundSecret->second.first; if (foundPresecrets == presecrets) { + ++_stats.hits; return foundSecret->second.second; } else { + ++_stats.misses; return {}; } } @@ -116,9 +128,20 @@ public: } } + /** + * Return metrics about the cache + */ + Stats getStats() const { + const stdx::lock_guard<Latch> lock(_hostToSecretsMutex); + Stats stats = _stats; + stats.count = _hostToSecrets.size(); + return stats; + } + private: mutable Mutex _hostToSecretsMutex = MONGO_MAKE_LATCH("SCRAMClientCache::_hostToSecretsMutex"); HostToSecretsMap _hostToSecrets; + mutable Stats _stats; }; } // namespace mongo diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index 3d72dd2dbd7..cc726b7385f 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -21,6 +21,7 @@ env.Library( env.Library( target='server_status_core', source=[ + 'server_status.cpp', 'server_status_internal.cpp', 'server_status_metric.cpp', ], @@ -32,7 +33,7 @@ env.Library( env.Library( target='server_status', source=[ - 'server_status.cpp', + 'server_status_command.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/commands', diff --git a/src/mongo/db/commands/server_status.cpp b/src/mongo/db/commands/server_status.cpp index a41f0545ac3..b7e6c5d3a24 100644 --- a/src/mongo/db/commands/server_status.cpp +++ b/src/mongo/db/commands/server_status.cpp @@ -27,274 +27,44 @@ * it in the license file. */ -#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kCommand - #include "mongo/platform/basic.h" -#include "mongo/db/auth/authorization_session.h" -#include "mongo/db/commands.h" #include "mongo/db/commands/server_status.h" + #include "mongo/db/commands/server_status_internal.h" #include "mongo/db/service_context.h" -#include "mongo/db/stats/counters.h" -#include "mongo/logv2/log.h" -#include "mongo/util/net/http_client.h" -#include "mongo/util/net/socket_utils.h" #include "mongo/util/version.h" namespace mongo { -using std::endl; -using std::map; -using std::string; -using std::stringstream; - namespace { constexpr auto kTimingSection = "timing"_sd; } // namespace -class CmdServerStatus : public BasicCommand { -public: - CmdServerStatus() : BasicCommand("serverStatus"), _started(Date_t::now()), _runCalled(false) {} - - virtual bool supportsWriteConcern(const BSONObj& cmd) const override { - return false; - } - AllowedOnSecondary secondaryAllowed(ServiceContext*) const override { - return AllowedOnSecondary::kAlways; - } - virtual bool allowsAfterClusterTime(const BSONObj& cmdObj) const override { - return false; - } - std::string help() const override { - return "returns lots of administrative server statistics"; - } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) const { - ActionSet actions; - actions.addAction(ActionType::serverStatus); - out->push_back(Privilege(ResourcePattern::forClusterResource(), actions)); - } - bool run(OperationContext* opCtx, - const string& dbname, - const BSONObj& cmdObj, - BSONObjBuilder& result) { - _runCalled.store(true); - - const auto service = opCtx->getServiceContext(); - const auto clock = service->getFastClockSource(); - const auto runStart = clock->now(); - BSONObjBuilder timeBuilder(256); - - const auto authSession = AuthorizationSession::get(Client::getCurrent()); - - // --- basic fields that are global - - result.append("host", prettyHostName()); - result.append("version", VersionInfoInterface::instance().version()); - result.append("process", serverGlobalParams.binaryName); - result.append("pid", ProcessId::getCurrent().asLongLong()); - result.append("uptime", (double)(time(nullptr) - serverGlobalParams.started)); - auto uptime = clock->now() - _started; - result.append("uptimeMillis", durationCount<Milliseconds>(uptime)); - result.append("uptimeEstimate", durationCount<Seconds>(uptime)); - result.appendDate("localTime", jsTime()); - - timeBuilder.appendNumber("after basic", - durationCount<Milliseconds>(clock->now() - runStart)); - - // --- all sections - - for (SectionMap::const_iterator i = _sections.begin(); i != _sections.end(); ++i) { - ServerStatusSection* section = i->second; - - std::vector<Privilege> requiredPrivileges; - section->addRequiredPrivileges(&requiredPrivileges); - if (!authSession->isAuthorizedForPrivileges(requiredPrivileges)) - continue; - - bool include = section->includeByDefault(); - const auto& elem = cmdObj[section->getSectionName()]; - if (elem.type()) { - include = elem.trueValue(); - } - - if (!include) { - continue; - } - - section->appendSection(opCtx, elem, &result); - timeBuilder.appendNumber( - static_cast<string>(str::stream() << "after " << section->getSectionName()), - durationCount<Milliseconds>(clock->now() - runStart)); - } - - // --- counters - bool includeMetricTree = MetricTree::theMetricTree != nullptr; - if (cmdObj["metrics"].type() && !cmdObj["metrics"].trueValue()) - includeMetricTree = false; - - if (includeMetricTree) { - MetricTree::theMetricTree->appendTo(result); - } - - // --- some hard coded global things hard to pull out - - auto runElapsed = clock->now() - runStart; - timeBuilder.appendNumber("at end", durationCount<Milliseconds>(runElapsed)); - if (runElapsed > Milliseconds(1000)) { - BSONObj t = timeBuilder.obj(); - LOGV2(20499, - "serverStatus was very slow: {timeStats}", - "serverStatus was very slow", - "timeStats"_attr = t); - - bool include_timing = true; - const auto& elem = cmdObj[kTimingSection]; - if (!elem.eoo()) { - include_timing = elem.trueValue(); - } - - if (include_timing) { - result.append(kTimingSection, t); - } - } - - return true; - } - - void addSection(ServerStatusSection* section) { - // Disallow adding a section named "timing" as it is reserved for the server status command. - dassert(section->getSectionName() != kTimingSection); - verify(!_runCalled.load()); - _sections[section->getSectionName()] = section; - } +ServerStatusSectionRegistry* ServerStatusSectionRegistry::get() { + static ServerStatusSectionRegistry instance; + return &instance; +} -private: - const Date_t _started; - AtomicWord<bool> _runCalled; +void ServerStatusSectionRegistry::addSection(ServerStatusSection* section) { + // Disallow adding a section named "timing" as it is reserved for the server status command. + dassert(section->getSectionName() != kTimingSection); + verify(!_runCalled.load()); + _sections[section->getSectionName()] = section; +} - typedef map<string, ServerStatusSection*> SectionMap; - SectionMap _sections; +ServerStatusSectionRegistry::SectionMap::const_iterator ServerStatusSectionRegistry::begin() { + _runCalled.store(true); + return _sections.begin(); }; -namespace { - -// This widget ensures that the serverStatus command is registered even if no -// server status sections are registered. - -const struct CmdServerStatusInstantiator { - explicit CmdServerStatusInstantiator() { - getInstance(); - } - - static CmdServerStatus& getInstance() { - static CmdServerStatus instance; - return instance; - } -} kDoNotMentionThisVariable; - -} // namespace - -ServerStatusSection::ServerStatusSection(const string& sectionName) : _sectionName(sectionName) { - CmdServerStatusInstantiator::getInstance().addSection(this); +ServerStatusSectionRegistry::SectionMap::const_iterator ServerStatusSectionRegistry::end() { + return _sections.end(); } -OpCounterServerStatusSection::OpCounterServerStatusSection(const string& sectionName, - OpCounters* counters) - : ServerStatusSection(sectionName), _counters(counters) {} - -BSONObj OpCounterServerStatusSection::generateSection(OperationContext* opCtx, - const BSONElement& configElement) const { - return _counters->getObj(); +ServerStatusSection::ServerStatusSection(const std::string& sectionName) + : _sectionName(sectionName) { + ServerStatusSectionRegistry::get()->addSection(this); } -OpCounterServerStatusSection globalOpCounterServerStatusSection("opcounters", &globalOpCounters); - - -namespace { - -// some universal sections - -class ExtraInfo : public ServerStatusSection { -public: - ExtraInfo() : ServerStatusSection("extra_info") {} - - bool includeByDefault() const override { - return true; - } - - BSONObj generateSection(OperationContext* opCtx, - const BSONElement& configElement) const override { - BSONObjBuilder bb; - - bb.append("note", "fields vary by platform"); - ProcessInfo p; - p.getExtraInfo(bb); - - return bb.obj(); - } - -} extraInfo; - -class Asserts : public ServerStatusSection { -public: - Asserts() : ServerStatusSection("asserts") {} - - bool includeByDefault() const override { - return true; - } - - BSONObj generateSection(OperationContext* opCtx, - const BSONElement& configElement) const override { - BSONObjBuilder asserts; - asserts.append("regular", assertionCount.regular.loadRelaxed()); - asserts.append("warning", assertionCount.warning.loadRelaxed()); - asserts.append("msg", assertionCount.msg.loadRelaxed()); - asserts.append("user", assertionCount.user.loadRelaxed()); - asserts.append("tripwire", assertionCount.tripwire.loadRelaxed()); - asserts.append("rollovers", assertionCount.rollovers.loadRelaxed()); - return asserts.obj(); - } - -} asserts; - -class MemBase : public ServerStatusMetric { -public: - MemBase() : ServerStatusMetric(".mem.bits") {} - virtual void appendAtLeaf(BSONObjBuilder& b) const { - b.append("bits", sizeof(int*) == 4 ? 32 : 64); - - ProcessInfo p; - int v = 0; - if (p.supported()) { - b.appendNumber("resident", p.getResidentSize()); - v = p.getVirtualMemorySize(); - b.appendNumber("virtual", v); - b.appendBool("supported", true); - } else { - b.append("note", "not all mem info support on this platform"); - b.appendBool("supported", false); - } - } -} memBase; - -class HttpClientServerStatus : public ServerStatusSection { -public: - HttpClientServerStatus() : ServerStatusSection("http_client") {} - - bool includeByDefault() const final { - return false; - } - - void addRequiredPrivileges(std::vector<Privilege>* out) final {} - - BSONObj generateSection(OperationContext*, const BSONElement& configElement) const final { - return HttpClient::getServerStatus(); - } -} httpClientServerStatus; - -} // namespace - } // namespace mongo diff --git a/src/mongo/db/commands/server_status.h b/src/mongo/db/commands/server_status.h index c803710bff6..d4b8b59e025 100644 --- a/src/mongo/db/commands/server_status.h +++ b/src/mongo/db/commands/server_status.h @@ -107,6 +107,30 @@ private: const std::string _sectionName; }; +/** + * Class the keeps a map of all registered status sections + */ +class ServerStatusSectionRegistry { +public: + using SectionMap = std::map<std::string, ServerStatusSection*>; + + static ServerStatusSectionRegistry* get(); + + /** + * Add a status section to the map. Called by ServerStatusSection constructor + */ + void addSection(ServerStatusSection* section); + + SectionMap::const_iterator begin(); + + SectionMap::const_iterator end(); + +private: + AtomicWord<bool> _runCalled{false}; + + SectionMap _sections; +}; + class OpCounterServerStatusSection : public ServerStatusSection { public: OpCounterServerStatusSection(const std::string& sectionName, OpCounters* counters); diff --git a/src/mongo/db/commands/server_status_command.cpp b/src/mongo/db/commands/server_status_command.cpp new file mode 100644 index 00000000000..f099766e564 --- /dev/null +++ b/src/mongo/db/commands/server_status_command.cpp @@ -0,0 +1,270 @@ +/** + * Copyright (C) 2021-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::kCommand + +#include "mongo/platform/basic.h" + +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/commands.h" +#include "mongo/db/commands/server_status.h" +#include "mongo/db/commands/server_status_internal.h" +#include "mongo/db/service_context.h" +#include "mongo/db/stats/counters.h" +#include "mongo/logv2/log.h" +#include "mongo/util/net/http_client.h" +#include "mongo/util/net/socket_utils.h" +#include "mongo/util/version.h" + +namespace mongo { +namespace { +constexpr auto kTimingSection = "timing"_sd; + +class CmdServerStatus : public BasicCommand { +public: + CmdServerStatus() : BasicCommand("serverStatus"), _started(Date_t::now()) {} + + bool supportsWriteConcern(const BSONObj& cmd) const final { + return false; + } + + AllowedOnSecondary secondaryAllowed(ServiceContext*) const final { + return AllowedOnSecondary::kAlways; + } + + bool allowsAfterClusterTime(const BSONObj& cmdObj) const final { + return false; + } + + std::string help() const final { + return "returns lots of administrative server statistics"; + } + + void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) const final { + ActionSet actions; + actions.addAction(ActionType::serverStatus); + out->push_back(Privilege(ResourcePattern::forClusterResource(), actions)); + } + + bool run(OperationContext* opCtx, + const std::string& dbname, + const BSONObj& cmdObj, + BSONObjBuilder& result) final { + const auto service = opCtx->getServiceContext(); + const auto clock = service->getFastClockSource(); + const auto runStart = clock->now(); + BSONObjBuilder timeBuilder(256); + + const auto authSession = AuthorizationSession::get(Client::getCurrent()); + + // --- basic fields that are global + + result.append("host", prettyHostName()); + result.append("version", VersionInfoInterface::instance().version()); + result.append("process", serverGlobalParams.binaryName); + result.append("pid", ProcessId::getCurrent().asLongLong()); + result.append("uptime", (double)(time(nullptr) - serverGlobalParams.started)); + auto uptime = clock->now() - _started; + result.append("uptimeMillis", durationCount<Milliseconds>(uptime)); + result.append("uptimeEstimate", durationCount<Seconds>(uptime)); + result.appendDate("localTime", jsTime()); + + timeBuilder.appendNumber("after basic", + durationCount<Milliseconds>(clock->now() - runStart)); + + // --- all sections + auto registry = ServerStatusSectionRegistry::get(); + for (auto i = registry->begin(); i != registry->end(); ++i) { + ServerStatusSection* section = i->second; + + std::vector<Privilege> requiredPrivileges; + section->addRequiredPrivileges(&requiredPrivileges); + if (!authSession->isAuthorizedForPrivileges(requiredPrivileges)) + continue; + + bool include = section->includeByDefault(); + const auto& elem = cmdObj[section->getSectionName()]; + if (elem.type()) { + include = elem.trueValue(); + } + + if (!include) { + continue; + } + + section->appendSection(opCtx, elem, &result); + timeBuilder.appendNumber( + static_cast<std::string>(str::stream() << "after " << section->getSectionName()), + durationCount<Milliseconds>(clock->now() - runStart)); + } + + // --- counters + bool includeMetricTree = MetricTree::theMetricTree != nullptr; + if (cmdObj["metrics"].type() && !cmdObj["metrics"].trueValue()) + includeMetricTree = false; + + if (includeMetricTree) { + MetricTree::theMetricTree->appendTo(result); + } + + // --- some hard coded global things hard to pull out + + auto runElapsed = clock->now() - runStart; + timeBuilder.appendNumber("at end", durationCount<Milliseconds>(runElapsed)); + if (runElapsed > Milliseconds(1000)) { + BSONObj t = timeBuilder.obj(); + LOGV2(20499, + "serverStatus was very slow: {timeStats}", + "serverStatus was very slow", + "timeStats"_attr = t); + + bool include_timing = true; + const auto& elem = cmdObj[kTimingSection]; + if (!elem.eoo()) { + include_timing = elem.trueValue(); + } + + if (include_timing) { + result.append(kTimingSection, t); + } + } + + return true; + } + +private: + const Date_t _started; +}; + +Command* serverStatusCommand; + +MONGO_INITIALIZER(CreateCmdServerStatus)(InitializerContext* context) { + serverStatusCommand = new CmdServerStatus(); +} + +} // namespace + +OpCounterServerStatusSection::OpCounterServerStatusSection(const std::string& sectionName, + OpCounters* counters) + : ServerStatusSection(sectionName), _counters(counters) {} + +BSONObj OpCounterServerStatusSection::generateSection(OperationContext* opCtx, + const BSONElement& configElement) const { + return _counters->getObj(); +} + +OpCounterServerStatusSection globalOpCounterServerStatusSection("opcounters", &globalOpCounters); + +namespace { + +// some universal sections + +class ExtraInfo : public ServerStatusSection { +public: + ExtraInfo() : ServerStatusSection("extra_info") {} + + bool includeByDefault() const override { + return true; + } + + BSONObj generateSection(OperationContext* opCtx, + const BSONElement& configElement) const override { + BSONObjBuilder bb; + + bb.append("note", "fields vary by platform"); + ProcessInfo p; + p.getExtraInfo(bb); + + return bb.obj(); + } + +} extraInfo; + +class Asserts : public ServerStatusSection { +public: + Asserts() : ServerStatusSection("asserts") {} + + bool includeByDefault() const override { + return true; + } + + BSONObj generateSection(OperationContext* opCtx, + const BSONElement& configElement) const override { + BSONObjBuilder asserts; + asserts.append("regular", assertionCount.regular.loadRelaxed()); + asserts.append("warning", assertionCount.warning.loadRelaxed()); + asserts.append("msg", assertionCount.msg.loadRelaxed()); + asserts.append("user", assertionCount.user.loadRelaxed()); + asserts.append("tripwire", assertionCount.tripwire.loadRelaxed()); + asserts.append("rollovers", assertionCount.rollovers.loadRelaxed()); + return asserts.obj(); + } + +} asserts; + +class MemBase : public ServerStatusMetric { +public: + MemBase() : ServerStatusMetric(".mem.bits") {} + virtual void appendAtLeaf(BSONObjBuilder& b) const { + b.append("bits", sizeof(int*) == 4 ? 32 : 64); + + ProcessInfo p; + int v = 0; + if (p.supported()) { + b.appendNumber("resident", p.getResidentSize()); + v = p.getVirtualMemorySize(); + b.appendNumber("virtual", v); + b.appendBool("supported", true); + } else { + b.append("note", "not all mem info support on this platform"); + b.appendBool("supported", false); + } + } +} memBase; + +class HttpClientServerStatus : public ServerStatusSection { +public: + HttpClientServerStatus() : ServerStatusSection("http_client") {} + + bool includeByDefault() const final { + return false; + } + + void addRequiredPrivileges(std::vector<Privilege>* out) final {} + + BSONObj generateSection(OperationContext*, const BSONElement& configElement) const final { + return HttpClient::getServerStatus(); + } +} httpClientServerStatus; + +} // namespace + +} // namespace mongo diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index 84741093996..1cb28229770 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -1289,11 +1289,11 @@ env.Library( "tenant_migration_server_status_section.cpp", ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/commands/server_status', "repl_server_parameters", ], LIBDEPS_PRIVATE=[ "$BUILD_DIR/mongo/base", + '$BUILD_DIR/mongo/db/commands/server_status_core', ], ) diff --git a/src/mongo/executor/connection_pool_tl.cpp b/src/mongo/executor/connection_pool_tl.cpp index 30383538b1a..547a968ed38 100644 --- a/src/mongo/executor/connection_pool_tl.cpp +++ b/src/mongo/executor/connection_pool_tl.cpp @@ -171,7 +171,7 @@ public: explicit TLConnectionSetupHook(executor::NetworkConnectionHook* hookToWrap, bool x509AuthOnly) : _wrappedHook(hookToWrap), _x509AuthOnly(x509AuthOnly) {} - BSONObj augmentIsMasterRequest(BSONObj cmdObj) override { + BSONObj augmentIsMasterRequest(const HostAndPort& remoteHost, BSONObj cmdObj) override { BSONObjBuilder bob(std::move(cmdObj)); bob.append("hangUpOnStepDown", false); if (internalSecurity.user) { @@ -181,7 +181,7 @@ public: if (_x509AuthOnly) { _speculativeAuthType = auth::SpeculativeAuthType::kAuthenticate; } else { - _speculativeAuthType = auth::speculateInternalAuth(&bob, &_session); + _speculativeAuthType = auth::speculateInternalAuth(remoteHost, &bob, &_session); } return bob.obj(); diff --git a/src/mongo/executor/network_connection_hook.h b/src/mongo/executor/network_connection_hook.h index 32186b985e6..04de5571cb6 100644 --- a/src/mongo/executor/network_connection_hook.h +++ b/src/mongo/executor/network_connection_hook.h @@ -58,7 +58,7 @@ public: * * By default this will just return the cmdObj passed in unaltered. */ - virtual BSONObj augmentIsMasterRequest(BSONObj cmdObj) { + virtual BSONObj augmentIsMasterRequest(const HostAndPort& remoteHost, BSONObj cmdObj) { return cmdObj; } |