diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 56502aa6690..bf03a0efbdd 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -573,6 +573,7 @@ target_sources (rippled PRIVATE src/ripple/rpc/handlers/FetchInfo.cpp src/ripple/rpc/handlers/GatewayBalances.cpp src/ripple/rpc/handlers/GetCounts.cpp + src/ripple/rpc/handlers/HealthCheck.cpp src/ripple/rpc/handlers/LedgerAccept.cpp src/ripple/rpc/handlers/LedgerCleanerHandler.cpp src/ripple/rpc/handlers/LedgerClosed.cpp diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index cfc824915d0..dadf0d7e617 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -147,6 +147,7 @@ void printHelp (const po::options_description& desc) " fetch_info [clear]\n" " gateway_balances [] [ [ ]]\n" " get_counts\n" + " health_check\n" " json \n" " ledger [|current|closed|validated] [full]\n" " ledger_accept\n" diff --git a/src/ripple/net/impl/RPCCall.cpp b/src/ripple/net/impl/RPCCall.cpp index bca8fefb5b7..a216423dfbc 100644 --- a/src/ripple/net/impl/RPCCall.cpp +++ b/src/ripple/net/impl/RPCCall.cpp @@ -1178,6 +1178,7 @@ class RPCParser { "fetch_info", &RPCParser::parseFetchInfo, 0, 1 }, { "gateway_balances", &RPCParser::parseGatewayBalances, 1, -1 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, + { "health_check", &RPCParser::parseAsIs, 0, 0 }, { "json", &RPCParser::parseJson, 2, 2 }, { "json2", &RPCParser::parseJson2, 1, 1 }, { "ledger", &RPCParser::parseLedger, 0, 2 }, diff --git a/src/ripple/rpc/handlers/Handlers.h b/src/ripple/rpc/handlers/Handlers.h index edf0acbdf57..3b14343b391 100644 --- a/src/ripple/rpc/handlers/Handlers.h +++ b/src/ripple/rpc/handlers/Handlers.h @@ -47,6 +47,7 @@ Json::Value doFee (RPC::JsonContext&); Json::Value doFetchInfo (RPC::JsonContext&); Json::Value doGatewayBalances (RPC::JsonContext&); Json::Value doGetCounts (RPC::JsonContext&); +Json::Value doHealthCheck (RPC::JsonContext&); Json::Value doLedgerAccept (RPC::JsonContext&); Json::Value doLedgerCleaner (RPC::JsonContext&); Json::Value doLedgerClosed (RPC::JsonContext&); diff --git a/src/ripple/rpc/handlers/HealthCheck.cpp b/src/ripple/rpc/handlers/HealthCheck.cpp new file mode 100644 index 00000000000..06128b7c83e --- /dev/null +++ b/src/ripple/rpc/handlers/HealthCheck.cpp @@ -0,0 +1,119 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012-2014 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +Json::Value +doHealthCheck(RPC::JsonContext& context) +{ + bool constexpr humanReadable = true; + bool constexpr noAdmin = false; + bool constexpr noCounters = false; + auto info = context.netOps.getServerInfo( + humanReadable, noAdmin, noCounters); + + int last_validated_ledger_age = std::numeric_limits::max(); + if (info.isMember("validated_ledger")) + last_validated_ledger_age = info["validated_ledger"]["age"].asInt(); + bool amendment_blocked = false; + if (info.isMember("amendment_blocked")) + amendment_blocked = true; + int number_peers = info["peers"].asInt(); + std::string server_state = info["server_state"].asString(); + auto load_factor = info["load_factor"].asDouble(); + + Json::Value ret = Json::objectValue; + enum {healthy, warning, critical}; + int health = healthy; + auto set_health = [&health](int state) + { + if (health < state) + health = state; + }; + + if (last_validated_ledger_age >= 7) + { + ret[jss::info]["validated_ledger"] = last_validated_ledger_age; + if (last_validated_ledger_age < 20) + set_health(warning); + else + set_health(critical); + } + + if (amendment_blocked) + { + ret[jss::info]["amendment_blocked"] = true; + set_health(critical); + } + + if (number_peers <= 7) + { + ret[jss::info]["peers"] = number_peers; + if (number_peers != 0) + set_health(warning); + else + set_health(critical); + } + + if (!(server_state == "full" || server_state == "validating" || + server_state == "proposing")) + { + ret[jss::info]["server_state"] = server_state; + if (server_state == "syncing" || server_state == "tracking" || + server_state == "connected") + { + set_health(warning); + } + else + set_health(critical); + } + + if (load_factor > 100) + { + ret[jss::info]["load_factor"] = load_factor; + if (load_factor < 1000) + set_health(warning); + else + set_health(critical); + } + + switch (health) + { + case healthy: + ret["health"] = "Healthy"; + break; + case warning: + ret["health"] = "Warning"; + break; + default: + ret["health"] = "Critical"; + break; + } + return ret; +} + +} // ripple diff --git a/src/ripple/rpc/impl/Handler.cpp b/src/ripple/rpc/impl/Handler.cpp index bfc6d7681c5..daa91d785b8 100644 --- a/src/ripple/rpc/impl/Handler.cpp +++ b/src/ripple/rpc/impl/Handler.cpp @@ -80,6 +80,7 @@ Handler const handlerArray[] { { "feature", byRef (&doFeature), Role::ADMIN, NO_CONDITION }, { "fee", byRef (&doFee), Role::USER, NEEDS_CURRENT_LEDGER }, { "fetch_info", byRef (&doFetchInfo), Role::ADMIN, NO_CONDITION }, + { "health_check", byRef (&doHealthCheck), Role::USER, NO_CONDITION }, { "ledger_accept", byRef (&doLedgerAccept), Role::ADMIN, NEEDS_CURRENT_LEDGER }, { "ledger_cleaner", byRef (&doLedgerCleaner), Role::ADMIN, NEEDS_NETWORK_CONNECTION }, { "ledger_closed", byRef (&doLedgerClosed), Role::USER, NO_CONDITION },