This is an automated email from the ASF dual-hosted git repository.
masaori pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new bf0eb13bcc Add traffic_ctl hostdb status (#12603)
bf0eb13bcc is described below
commit bf0eb13bcca0e5e1387b1cf92ad0bfa07db39593
Author: Masaori Koshiba <[email protected]>
AuthorDate: Thu Nov 6 07:29:05 2025 +0900
Add traffic_ctl hostdb status (#12603)
* Add traffic_ctl hostdb status
* Fix descriptions
* Set default string to UNKNOWN
* Cleanup
* Remove default for HostDBType
* Fix return-type error
---
doc/appendices/command-line/traffic_ctl.en.rst | 14 +-
include/iocore/hostdb/HostDBProcessor.h | 9 ++
include/mgmt/rpc/handlers/hostdb/HostDB.h | 28 ++++
src/mgmt/rpc/CMakeLists.txt | 1 +
src/mgmt/rpc/handlers/hostdb/HostDB.cc | 175 +++++++++++++++++++++++++
src/mgmt/rpc/schema/hostdb_status_schema.json | 115 ++++++++++++++++
src/traffic_ctl/CtrlCommands.cc | 22 ++++
src/traffic_ctl/CtrlCommands.h | 11 ++
src/traffic_ctl/CtrlPrinters.cc | 7 +-
src/traffic_ctl/CtrlPrinters.h | 8 ++
src/traffic_ctl/jsonrpc/CtrlRPCRequests.h | 12 ++
src/traffic_ctl/traffic_ctl.cc | 7 +
src/traffic_server/RpcAdminPubHandlers.cc | 5 +
13 files changed, 412 insertions(+), 2 deletions(-)
diff --git a/doc/appendices/command-line/traffic_ctl.en.rst
b/doc/appendices/command-line/traffic_ctl.en.rst
index c6009a4964..6a5b75c5bf 100644
--- a/doc/appendices/command-line/traffic_ctl.en.rst
+++ b/doc/appendices/command-line/traffic_ctl.en.rst
@@ -52,7 +52,9 @@ of subcommands that control different aspects of Traffic
Server:
:program:`traffic_ctl plugin`
Interact with plugins.
:program:`traffic_ctl host`
- Manipulate host status. parents for now but will be expanded to origins.
+ Manipulate host status.
+:program:`traffic_ctl hostdb`
+ Manipulate HostDB status.
:program:`traffic_ctl rpc`
Interact directly with the |RPC| server in |TS|
@@ -560,6 +562,16 @@ records may be viewed using the :program:`traffic_ctl host
status` command.
.. _traffic_ctl_rpc:
+traffic_ctl hostdb
+------------------
+.. program:: traffic_ctl hostdb
+
+.. option:: status
+
+ :ref:`admin_lookup_records`
+
+ Get the current status of HostDB.
+
traffic_ctl rpc
---------------
.. program:: traffic_ctl rpc
diff --git a/include/iocore/hostdb/HostDBProcessor.h
b/include/iocore/hostdb/HostDBProcessor.h
index 350802c0d0..fded833c50 100644
--- a/include/iocore/hostdb/HostDBProcessor.h
+++ b/include/iocore/hostdb/HostDBProcessor.h
@@ -393,6 +393,9 @@ public:
/// Get the array of info instances.
swoc::MemSpan<HostDBInfo> rr_info();
+ /// Get the array of info instances to read
+ swoc::MemSpan<const HostDBInfo> rr_info() const;
+
/** Find a host record by IP address.
*
* @param addr Address key.
@@ -784,6 +787,12 @@ HostDBRecord::rr_info()
return {this->apply_offset<HostDBInfo>(rr_offset), rr_count};
}
+inline swoc::MemSpan<const HostDBInfo>
+HostDBRecord::rr_info() const
+{
+ return {this->apply_offset<const HostDBInfo>(rr_offset), rr_count};
+}
+
inline bool
HostDBRecord::is_failed() const
{
diff --git a/include/mgmt/rpc/handlers/hostdb/HostDB.h
b/include/mgmt/rpc/handlers/hostdb/HostDB.h
new file mode 100644
index 0000000000..be65f317ad
--- /dev/null
+++ b/include/mgmt/rpc/handlers/hostdb/HostDB.h
@@ -0,0 +1,28 @@
+/* @file
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#pragma once
+
+#include "mgmt/rpc/jsonrpc/JsonRPCManager.h"
+
+namespace rpc::handlers::hostdb
+{
+swoc::Rv<YAML::Node> get_hostdb_status(std::string_view const &id, YAML::Node
const &);
+} // namespace rpc::handlers::hostdb
diff --git a/src/mgmt/rpc/CMakeLists.txt b/src/mgmt/rpc/CMakeLists.txt
index 0b8f3e3fb1..bee0f8929f 100644
--- a/src/mgmt/rpc/CMakeLists.txt
+++ b/src/mgmt/rpc/CMakeLists.txt
@@ -42,6 +42,7 @@ add_library(
handlers/common/ErrorUtils.cc
handlers/common/RecordsUtils.cc
handlers/config/Configuration.cc
+ handlers/hostdb/HostDB.cc
handlers/records/Records.cc
handlers/storage/Storage.cc
handlers/server/Server.cc
diff --git a/src/mgmt/rpc/handlers/hostdb/HostDB.cc
b/src/mgmt/rpc/handlers/hostdb/HostDB.cc
new file mode 100644
index 0000000000..1c720299a1
--- /dev/null
+++ b/src/mgmt/rpc/handlers/hostdb/HostDB.cc
@@ -0,0 +1,175 @@
+/**
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "mgmt/rpc/handlers/hostdb/HostDB.h"
+#include "mgmt/rpc/handlers/common/ErrorUtils.h"
+
+#include "iocore/hostdb/HostDBProcessor.h"
+#include "../src/iocore/hostdb/P_HostDBProcessor.h"
+#include "swoc/MemSpan.h"
+#include "tsutil/TsSharedMutex.h"
+#include "yaml-cpp/node/node.h"
+#include <shared_mutex>
+#include <string>
+
+namespace
+{
+DbgCtl dbg_ctl_rpc_server{"rpc.server"};
+DbgCtl dbg_ctl_rpc_handler_server{"rpc.handler.hostdb"};
+
+constexpr std::string_view
+str(HostDBType type)
+{
+ // No default to find HostDBType change
+ switch (type) {
+ case HostDBType::ADDR:
+ return "ADDR";
+ case HostDBType::SRV:
+ return "SRV";
+ case HostDBType::HOST:
+ return "HOST";
+ case HostDBType::UNSPEC:
+ return "UNSPEC";
+ }
+
+ return "";
+}
+
+constexpr std::string_view
+str(sa_family_t type)
+{
+ switch (type) {
+ case AF_UNIX:
+ return "AF_UNIX";
+ case AF_INET:
+ return "AF_INET";
+ case AF_INET6:
+ return "AF_INET6";
+ case AF_UNSPEC:
+ return "UNSPEC";
+ default:
+ return "UNKNOWN";
+ }
+}
+} // end anonymous namespace
+
+namespace YAML
+{
+template <> struct convert<HostDBCache> {
+ static Node
+ encode(const HostDBCache *const hostDB)
+ {
+ Node partitions;
+ for (size_t i = 0; i < hostDB->refcountcache->partition_count(); i++) {
+ auto &partition =
hostDB->refcountcache->get_partition(i);
+ std::vector<RefCountCacheHashEntry *> partition_entries;
+
+ {
+ std::shared_lock<ts::shared_mutex> shared_lock{partition.lock};
+ partition_entries.reserve(partition.count());
+ partition.copy(partition_entries);
+ }
+
+ Node partition_node;
+ partition_node["id"] = i;
+
+ for (RefCountCacheHashEntry *entry : partition_entries) {
+ HostDBRecord *record = static_cast<HostDBRecord *>(entry->item.get());
+ partition_node["records"].push_back(*record);
+ }
+
+ partitions.push_back(partition_node);
+ }
+
+ auto &version = AppVersionInfo::get_version();
+
+ Node node;
+ node["metadata"]["timestamp"] =
+
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+ node["metadata"]["version"] = version.full_version();
+ node["partitions"] = partitions;
+
+ return node;
+ }
+};
+
+template <> struct convert<HostDBRecord> {
+ static Node
+ encode(const HostDBRecord &record)
+ {
+ Node metadata;
+ metadata["name"] = record.name();
+ metadata["type"] = str(record.record_type);
+ metadata["af_familiy"] = str(record.af_family);
+ metadata["failed"] = record.is_failed();
+ metadata["ip_timestamp"] = record.ip_timestamp.time_since_epoch().count();
+
+ Node node;
+ node["metadata"] = metadata;
+
+ swoc::MemSpan<const HostDBInfo> span = record.rr_info();
+ for (const HostDBInfo &info : span) {
+ YAML::Node info_node;
+ if (record.is_srv()) {
+ YAML::Node srv_node;
+ srv_node["weight"] = info.data.srv.srv_weight;
+ srv_node["priority"] = info.data.srv.srv_priority;
+ srv_node["port"] = info.data.srv.srv_port;
+ srv_node["target"] = info.srvname();
+
+ info_node["srv"] = srv_node;
+ } else {
+ char buf[INET6_ADDRSTRLEN];
+ info.data.ip.toString(buf, sizeof(buf));
+
+ info_node["ip"] = std::string(buf);
+ }
+
+ info_node["health"]["last_failure"] =
info.last_failure.load().time_since_epoch().count();
+ info_node["health"]["fail_count"] =
static_cast<int>(info.fail_count.load());
+
+ node["info"].push_back(info_node);
+ }
+
+ return node;
+ }
+};
+} // namespace YAML
+
+namespace rpc::handlers::hostdb
+{
+namespace err = rpc::handlers::errors;
+
+swoc::Rv<YAML::Node>
+get_hostdb_status(std::string_view const & /* params ATS_UNUSED */, YAML::Node
const & /* params ATS_UNUSED */)
+{
+ swoc::Rv<YAML::Node> resp;
+ try {
+ YAML::Node data =
YAML::convert<HostDBCache>::encode(hostDBProcessor.cache());
+
+ resp.result()["data"] = data;
+ } catch (std::exception const &ex) {
+ resp.errata()
+ .assign(std::error_code{errors::Codes::SERVER})
+ .note("Error found when calling get_hostdb_status API: {}", ex.what());
+ }
+ return resp;
+}
+} // namespace rpc::handlers::hostdb
diff --git a/src/mgmt/rpc/schema/hostdb_status_schema.json
b/src/mgmt/rpc/schema/hostdb_status_schema.json
new file mode 100644
index 0000000000..c395250601
--- /dev/null
+++ b/src/mgmt/rpc/schema/hostdb_status_schema.json
@@ -0,0 +1,115 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "HostDB info definition",
+ "description":"This is the definition expected for a RPC hostdb status
request. This should be used to obtain information of HostDB from Traffic
Server Licensed under Apache V2 https://www.apache.org/licenses/LICENSE-2.0",
+ "type": "object",
+ "properties": {
+ "metadata": {
+ "type": "object",
+ "properties": {
+ "timestamp": {
+ "type": "integer"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ },
+ "partitions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer",
+ "description": "Partition identifier"
+ },
+ "records": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "metadata": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": ["UNSPEC", "ADDR", "SRV", "HOST"]
+ },
+ "af_family": {
+ "type": "string",
+ "enum": ["UNSPEC", "UNIX", "AF_INET", "AF_INET6"]
+ },
+ "name": {
+ "type": "string",
+ "description": "Query hostname or resolved name"
+ },
+ "failed": {
+ "type": "boolean"
+ },
+ "ip_timestamp": {
+ "type": "integer"
+ }
+ }
+ },
+ "info": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "ip": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "type": "object",
+ "properties": {
+ "srv": {
+ "type": "object",
+ "properties": {
+ "weight": {
+ "type": "integer"
+ },
+ "priority": {
+ "type": "integer"
+ },
+ "port": {
+ "type": "integer"
+ },
+ "target": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "health": {
+ "type": "object",
+ "properties": {
+ "last_failure": {
+ "type": "integer"
+ },
+ "fail_count": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/traffic_ctl/CtrlCommands.cc b/src/traffic_ctl/CtrlCommands.cc
index 17efd4439d..6d7a386e27 100644
--- a/src/traffic_ctl/CtrlCommands.cc
+++ b/src/traffic_ctl/CtrlCommands.cc
@@ -433,6 +433,28 @@ HostCommand::status_up()
_printer->write_output(response);
}
//------------------------------------------------------------------------------------------------------------------------------------
+HostDBCommand::HostDBCommand(ts::Arguments *args) : CtrlCommand(args)
+{
+ BasePrinter::Options printOpts{parse_print_opts(args)};
+ if (get_parsed_arguments()->get(STATUS_STR)) {
+ _printer = std::make_unique<HostDBStatusPrinter>(printOpts);
+ _invoked_func = [&]() { status_get(); };
+ }
+}
+
+void
+HostDBCommand::status_get()
+{
+ auto const &data = get_parsed_arguments()->get(STATUS_STR);
+ HostDBGetStatusRequest request{
+ {std::begin(data), std::end(data)}
+ };
+
+ auto response = invoke_rpc(request);
+
+ _printer->write_output(response);
+}
+//------------------------------------------------------------------------------------------------------------------------------------
PluginCommand::PluginCommand(ts::Arguments *args) : CtrlCommand(args)
{
if (get_parsed_arguments()->get(MSG_STR)) {
diff --git a/src/traffic_ctl/CtrlCommands.h b/src/traffic_ctl/CtrlCommands.h
index cc3a6ad8fa..ebf1e1eafe 100644
--- a/src/traffic_ctl/CtrlCommands.h
+++ b/src/traffic_ctl/CtrlCommands.h
@@ -178,6 +178,17 @@ private:
void status_up();
};
//
-----------------------------------------------------------------------------------------------------------------------------------
+class HostDBCommand : public CtrlCommand
+{
+public:
+ HostDBCommand(ts::Arguments *args);
+
+private:
+ static inline const std::string STATUS_STR{"status"};
+
+ void status_get();
+};
+//
-----------------------------------------------------------------------------------------------------------------------------------
class PluginCommand : public CtrlCommand
{
public:
diff --git a/src/traffic_ctl/CtrlPrinters.cc b/src/traffic_ctl/CtrlPrinters.cc
index 40fc02971e..55e51de20e 100644
--- a/src/traffic_ctl/CtrlPrinters.cc
+++ b/src/traffic_ctl/CtrlPrinters.cc
@@ -314,7 +314,12 @@ SetHostStatusPrinter::write_output([[maybe_unused]]
YAML::Node const &result)
// do nothing.
}
//------------------------------------------------------------------------------------------------------------------------------------
-
+void
+HostDBStatusPrinter::write_output(YAML::Node const &result)
+{
+ write_output_json(result["data"] ? result["data"] : result);
+}
+//-------------------------------------------------------------------------------------------------------------------------------------
void
CacheDiskStoragePrinter::write_output(YAML::Node const &result)
{
diff --git a/src/traffic_ctl/CtrlPrinters.h b/src/traffic_ctl/CtrlPrinters.h
index 33c2241055..0c724942d8 100644
--- a/src/traffic_ctl/CtrlPrinters.h
+++ b/src/traffic_ctl/CtrlPrinters.h
@@ -261,6 +261,14 @@ public:
SetHostStatusPrinter(BasePrinter::Options opt) : BasePrinter(opt) {}
};
//------------------------------------------------------------------------------------------------------------------------------------
+class HostDBStatusPrinter : public BasePrinter
+{
+ void write_output(YAML::Node const &result) override;
+
+public:
+ HostDBStatusPrinter(BasePrinter::Options opt) : BasePrinter(opt) {}
+};
+//------------------------------------------------------------------------------------------------------------------------------------
class CacheDiskStoragePrinter : public BasePrinter
{
void write_output(YAML::Node const &result) override;
diff --git a/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
b/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
index 862ecc68b3..acf1118873 100644
--- a/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
+++ b/src/traffic_ctl/jsonrpc/CtrlRPCRequests.h
@@ -125,6 +125,18 @@ struct HostGetStatusRequest : shared::rpc::ClientRequest {
}
};
//------------------------------------------------------------------------------------------------------------------------------------
+struct HostDBGetStatusRequest : shared::rpc::ClientRequest {
+ using super = shared::rpc::ClientRequest;
+ using Params = std::vector<std::string>;
+ HostDBGetStatusRequest(Params p) { super::params = std::move(p); }
+
+ std::string
+ get_method() const override
+ {
+ return "get_hostdb_status";
+ }
+};
+//------------------------------------------------------------------------------------------------------------------------------------
struct BasicPluginMessageRequest : shared::rpc::ClientRequest {
using super = BasicPluginMessageRequest;
struct Params {
diff --git a/src/traffic_ctl/traffic_ctl.cc b/src/traffic_ctl/traffic_ctl.cc
index e9b8d73445..673b1906f7 100644
--- a/src/traffic_ctl/traffic_ctl.cc
+++ b/src/traffic_ctl/traffic_ctl.cc
@@ -88,6 +88,7 @@ main([[maybe_unused]] int argc, const char **argv)
auto &storage_command = parser.add_command("storage", "Manipulate cache
storage").require_commands();
auto &plugin_command = parser.add_command("plugin", "Interact with
plugins").require_commands();
auto &host_command = parser.add_command("host", "Interact with host
status").require_commands();
+ auto &hostdb_command = parser.add_command("hostdb", "Interact with
HostDB status").require_commands();
auto &direct_rpc_command = parser.add_command("rpc", "Interact with the rpc
api").require_commands();
// config commands
@@ -159,6 +160,10 @@ main([[maybe_unused]] int argc, const char **argv)
"", 1, "0")
.add_option("--interval", "-i", "Wait interval seconds between sending
each metric request. Minimum value is 1s.", "", 1, "5");
+ // hostdb commands
+ hostdb_command.add_command("status", "Get HostDB info", "",
MORE_THAN_ZERO_ARG_N, [&]() { command->execute(); })
+ .add_example_usage("traffic_ctl hostdb status");
+
// plugin command
plugin_command
.add_command("msg", "Send message to plugins - a TAG and the message
DATA(optional)", "", MORE_THAN_ONE_ARG_N,
@@ -235,6 +240,8 @@ main([[maybe_unused]] int argc, const char **argv)
command = std::make_shared<PluginCommand>(&args);
} else if (args.get("host")) {
command = std::make_shared<HostCommand>(&args);
+ } else if (args.get("hostdb")) {
+ command = std::make_shared<HostDBCommand>(&args);
} else if (args.get("rpc")) {
command = std::make_shared<DirectRPCCommand>(&args);
}
diff --git a/src/traffic_server/RpcAdminPubHandlers.cc
b/src/traffic_server/RpcAdminPubHandlers.cc
index 4269a37399..4ab2706f44 100644
--- a/src/traffic_server/RpcAdminPubHandlers.cc
+++ b/src/traffic_server/RpcAdminPubHandlers.cc
@@ -22,6 +22,7 @@
// Admin API Implementation headers.
#include "mgmt/rpc/handlers/config/Configuration.h"
+#include "mgmt/rpc/handlers/hostdb/HostDB.h"
#include "mgmt/rpc/handlers/records/Records.h"
#include "mgmt/rpc/handlers/storage/Storage.h"
#include "mgmt/rpc/handlers/server/Server.h"
@@ -39,6 +40,10 @@ register_admin_jsonrpc_handlers()
{{rpc::RESTRICTED_API}});
rpc::add_method_handler("admin_config_reload", &reload_config,
&core_ats_rpc_service_provider_handle, {{rpc::RESTRICTED_API}});
+ // HostDB
+ using namespace rpc::handlers::hostdb;
+ rpc::add_method_handler("get_hostdb_status", &get_hostdb_status,
&core_ats_rpc_service_provider_handle, {{rpc::RESTRICTED_API}});
+
// Records
using namespace rpc::handlers::records;
rpc::add_method_handler("admin_lookup_records", &lookup_records,
&core_ats_rpc_service_provider_handle,