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,

Reply via email to