diff --git a/src/server/main_service.cc b/src/server/main_service.cc index 259b8c2a9..6b9d0ea80 100644 --- a/src/server/main_service.cc +++ b/src/server/main_service.cc @@ -549,6 +549,95 @@ void TxTable(const http::QueryArgs& args, HttpContext* send) { send->Invoke(std::move(resp)); } +void ClusterHtmlPage(const http::QueryArgs& args, HttpContext* send, ClusterFamily* cluster) { + http::StringResponse resp = http::MakeStringResponse(h2::status::ok); + resp.body() = R"( + + + + + +

Cluster Info

+)"; + + auto print_kv = [&](string_view k, string_view v) { + resp.body() += absl::StrCat("
", k, + "", v, "
\n"); + }; + + auto print_kb = [&](string_view k, bool v) { print_kv(k, v ? "True" : "False"); }; + + print_kv("Mode", ClusterConfig::IsEmulated() ? "Emulated" + : ClusterConfig::IsEnabled() ? "Enabled" + : "Disabled"); + + if (ClusterConfig::IsEnabledOrEmulated()) { + print_kb("Lock on hashtags", KeyLockArgs::IsLockHashTagEnabled()); + } + + if (ClusterConfig::IsEnabled()) { + if (cluster->cluster_config() == nullptr) { + resp.body() += "

Not yet configured.

\n"; + } else { + auto config = cluster->cluster_config()->GetConfig(); + for (const auto& shard : config) { + resp.body() += "
\n"; + resp.body() += "

Master

\n"; + print_kv("ID", shard.master.id); + print_kv("IP", shard.master.ip); + print_kv("Port", absl::StrCat(shard.master.port)); + + resp.body() += "

Replicas

\n"; + if (shard.replicas.empty()) { + resp.body() += "

None

\n"; + } else { + for (const auto& replica : shard.replicas) { + resp.body() += "

Replica

\n"; + print_kv("ID", replica.id); + print_kv("IP", replica.ip); + print_kv("Port", absl::StrCat(replica.port)); + } + } + + resp.body() += "

Slots

\n"; + for (const auto& slot : shard.slot_ranges) { + resp.body() += + absl::StrCat("
[", slot.start, + "-", slot.end, "]
"); + } + + resp.body() += "
\n"; + } + } + } + + resp.body() += " \n\n"; + send->Invoke(std::move(resp)); +} + enum class ExecEvalState { NONE = 0, ALL = 1, @@ -2201,6 +2290,9 @@ void Service::ConfigureHttpHandlers(util::HttpListenerBase* base, bool is_privil server_family_.ConfigureMetrics(base); base->RegisterCb("/txz", TxTable); base->RegisterCb("/topkeys", Topkeys); + base->RegisterCb("/clusterz", [this](const http::QueryArgs& args, HttpContext* send) { + return ClusterHtmlPage(args, send, &cluster_family_); + }); } void Service::OnClose(facade::ConnectionContext* cntx) {