This is an automated email from the ASF dual-hosted git repository.
dmeden 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 ab25c40a07 jsonrpc - Implement handler to fetch connection tracker
information. (#12260)
ab25c40a07 is described below
commit ab25c40a075b05db10afaa7e2d3d1cd56303b449
Author: Damian Meden <[email protected]>
AuthorDate: Wed Jun 11 11:00:33 2025 +0200
jsonrpc - Implement handler to fetch connection tracker information.
(#12260)
This pr implements the rpc handler, and some unit tests to validate
basic output.
---
doc/developer-guide/jsonrpc/jsonrpc-api.en.rst | 84 ++++++++++++++++++++++
include/mgmt/rpc/handlers/server/Server.h | 1 +
src/mgmt/rpc/handlers/server/Server.cc | 81 +++++++++++++++++++++
src/traffic_server/RpcAdminPubHandlers.cc | 3 +-
.../traffic_ctl/traffic_ctl_server_output.test.py | 16 +++++
.../traffic_ctl/traffic_ctl_test_utils.py | 40 +++++++++++
6 files changed, 224 insertions(+), 1 deletion(-)
diff --git a/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
b/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
index c1b7986345..1a966da87b 100644
--- a/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
+++ b/doc/developer-guide/jsonrpc/jsonrpc-api.en.rst
@@ -469,6 +469,8 @@ JSONRPC API
* `filemanager.get_files_registry`_
+* `get_connection_tracker_info`_
+
.. _jsonapi-management-records:
@@ -1667,6 +1669,88 @@ Response:
}
}
+.. _get_connection_tracker_info:
+
+get_connection_tracker_info
+----------------------------
+
+|method|
+
+Description
+~~~~~~~~~~~
+
+Get inbound and outbound connection tracking data from |TS|.
+
+Parameters
+~~~~~~~~~~
+
+======================= =============
==================================================================================
+Field Type Description
+======================= =============
==================================================================================
+``table`` |str| Specifies which group table to query,
``inbound`` or ``outbound``. If value is
+ ``both`` then both tables will be added
to the response. The default is ``outbound``.
+======================= =============
==================================================================================
+
+You can query this api using `invoke` functionality from
:ref:`traffic_ctl_rpc`.
+
+ .. code-block:: bash
+ :linenos:
+
+ $ traffic_ctl rpc invoke get_connection_tracker_info -p 'table: both'
-f json
+
+
+.. note::
+
+ JSON output can be formatted by any tool like `jq`.
+
+Result
+~~~~~~
+
+The response will contain inbound/outbound info or an error.
:ref:`jsonrpc-node-errors`.
+
+Response examples
+~~~~~~~~~~~~~~~~~
+
+ .. code-block:: json
+ :linenos:
+
+ {
+ "id":"f4477ac4-0d44-11eb-958d-001fc69cc946",
+ "jsonrpc":"2.0",
+ "result":{
+ "outbound":{
+ "count":"1",
+ "list":[
+ {
+ "type":"both",
+ "ip":"127.0.0.1:80",
+ "fqdn":"127.0.0.1",
+ "current":"8",
+ "max":"8",
+ "blocked":"0",
+ "alert":"0"
+ }
+ ]
+ },
+ "inbound":{
+ "count":"1",
+ "list":[
+ {
+ "type":"ip",
+ "ip":"127.0.0.1:34226",
+ "fqdn":"",
+ "current":"2",
+ "max":"0",
+ "blocked":"0",
+ "alert":"0"
+ }
+ ]
+ }
+ }
+ }
+
+
+
See also
========
diff --git a/include/mgmt/rpc/handlers/server/Server.h
b/include/mgmt/rpc/handlers/server/Server.h
index face7053f4..fec31c66bf 100644
--- a/include/mgmt/rpc/handlers/server/Server.h
+++ b/include/mgmt/rpc/handlers/server/Server.h
@@ -28,5 +28,6 @@ swoc::Rv<YAML::Node> server_start_drain(std::string_view
const &id, YAML::Node c
swoc::Rv<YAML::Node> server_stop_drain(std::string_view const &id, YAML::Node
const &);
void server_shutdown(YAML::Node const &);
swoc::Rv<YAML::Node> get_server_status(std::string_view const &id, YAML::Node
const &);
+swoc::Rv<YAML::Node> get_connection_tracker_info(std::string_view const &id,
YAML::Node const ¶ms);
} // namespace rpc::handlers::server
diff --git a/src/mgmt/rpc/handlers/server/Server.cc
b/src/mgmt/rpc/handlers/server/Server.cc
index c032e7a7ff..bd251a8f52 100644
--- a/src/mgmt/rpc/handlers/server/Server.cc
+++ b/src/mgmt/rpc/handlers/server/Server.cc
@@ -19,6 +19,7 @@
*/
#include "../../../../iocore/cache/P_CacheDir.h"
+#include "iocore/net/ConnectionTracker.h"
#include "mgmt/rpc/handlers/server/Server.h"
#include "mgmt/rpc/handlers/common/ErrorUtils.h"
#include "mgmt/rpc/handlers/common/Utils.h"
@@ -42,6 +43,33 @@ namespace field_names
struct DrainInfo {
bool noNewConnections{false};
};
+
+struct ConnectionTrackingInfo {
+ enum TableFlags {
+ NOT_SET = 0,
+ INBOUND = 1 << 0, // Inbound table
+ OUTBOUND = 1 << 1 // Outbound table
+ };
+ TableFlags table{TableFlags::OUTBOUND}; // default.
+};
+using TFLags = ConnectionTrackingInfo::TableFlags;
+constexpr TFLags
+operator|(const TFLags rhs, const TFLags lhs)
+{
+ return static_cast<TFLags>(static_cast<uint32_t>(rhs) |
static_cast<uint32_t>(lhs));
+}
+
+constexpr TFLags &
+operator|=(TFLags &rhs, TFLags lhs)
+{
+ return rhs = rhs | lhs;
+}
+
+constexpr TFLags
+operator&(TFLags rhs, TFLags lhs)
+{
+ return static_cast<TFLags>(static_cast<uint32_t>(rhs) &
static_cast<uint32_t>(lhs));
+}
} // namespace rpc::handlers::server
namespace YAML
{
@@ -61,6 +89,31 @@ template <> struct convert<rpc::handlers::server::DrainInfo>
{
return true;
}
};
+template <> struct convert<rpc::handlers::server::ConnectionTrackingInfo> {
+ static constexpr auto table{"table"};
+ static bool
+ decode(const Node &node, rpc::handlers::server::ConnectionTrackingInfo &rhs)
+ {
+ namespace field = rpc::handlers::server::field_names;
+ if (!node.IsMap()) {
+ return false;
+ }
+ // optional
+ if (auto n = node[table]; !n.IsNull()) {
+ auto const &val = n.as<std::string>();
+ if (val == "both") {
+ rhs.table = rpc::handlers::server::TFLags::INBOUND |
rpc::handlers::server::TFLags::OUTBOUND;
+ } else if (val == "inbound") {
+ rhs.table = rpc::handlers::server::TFLags::INBOUND;
+ } else if (val == "outbound") {
+ rhs.table = rpc::handlers::server::TFLags::OUTBOUND;
+ } else {
+ throw std::runtime_error("Invalid table type. Use
[both|inbound|outbound]");
+ }
+ }
+ return true;
+ }
+};
} // namespace YAML
namespace rpc::handlers::server
@@ -150,4 +203,32 @@ get_server_status(std::string_view const & /* params
ATS_UNUSED */, YAML::Node c
}
return resp;
}
+
+swoc::Rv<YAML::Node>
+get_connection_tracker_info(std::string_view const & /* params ATS_UNUSED */,
YAML::Node const ¶ms)
+{
+ swoc::Rv<YAML::Node> resp;
+ try {
+ ConnectionTrackingInfo p;
+ if (!params.IsNull()) {
+ p = params.as<ConnectionTrackingInfo>();
+ }
+
+ if (p.table & TFLags::OUTBOUND) {
+ std::string json{ConnectionTracker::outbound_to_json_string()};
+ resp.result()["outbound"] = YAML::Load(json);
+ }
+
+ if (p.table & TFLags::INBOUND) {
+ std::string json{ConnectionTracker::inbound_to_json_string()};
+ resp.result()["inbound"] = YAML::Load(json);
+ }
+
+ } catch (std::exception const &ex) {
+ resp.errata()
+ .assign(std::error_code{errors::Codes::SERVER})
+ .note("Error found when calling get_connection_tracker_info API: {}",
ex.what());
+ }
+ return resp;
+}
} // namespace rpc::handlers::server
diff --git a/src/traffic_server/RpcAdminPubHandlers.cc
b/src/traffic_server/RpcAdminPubHandlers.cc
index 7e6e0fdff7..4269a37399 100644
--- a/src/traffic_server/RpcAdminPubHandlers.cc
+++ b/src/traffic_server/RpcAdminPubHandlers.cc
@@ -57,7 +57,8 @@ register_admin_jsonrpc_handlers()
{{rpc::RESTRICTED_API}});
rpc::add_method_handler("get_server_status", &get_server_status,
&core_ats_rpc_service_provider_handle,
{{rpc::NON_RESTRICTED_API}});
-
+ rpc::add_method_handler("get_connection_tracker_info",
&get_connection_tracker_info, &core_ats_rpc_service_provider_handle,
+ {{rpc::NON_RESTRICTED_API}});
// storage
using namespace rpc::handlers::storage;
rpc::add_method_handler("admin_storage_set_device_offline",
&set_storage_offline, &core_ats_rpc_service_provider_handle,
diff --git a/tests/gold_tests/traffic_ctl/traffic_ctl_server_output.test.py
b/tests/gold_tests/traffic_ctl/traffic_ctl_server_output.test.py
index 747eb2a97f..c6362de166 100644
--- a/tests/gold_tests/traffic_ctl/traffic_ctl_server_output.test.py
+++ b/tests/gold_tests/traffic_ctl/traffic_ctl_server_output.test.py
@@ -43,3 +43,19 @@ traffic_ctl.server().drain().exec()
traffic_ctl.server().status().validate_with_text(
'{"initialized_done": "true", "is_ssl_handshaking_stopped": "false",
"is_draining": "true", "is_event_system_shut_down": "false"}'
)
+
+# Get basic and empty connection tracker info.
+traffic_ctl.rpc().invoke(
+ handler="get_connection_tracker_info", params='"table:
both"').validate_result_with_text(
+ '{"outbound": {"count": "0", "list": []}, "inbound": {"count": "0",
"list": []}}')
+# default = outbound only
+traffic_ctl.rpc().invoke(
+
handler="get_connection_tracker_info").validate_result_with_text('{"outbound":
{"count": "0", "list": []}}')
+# requets inbound oonly
+traffic_ctl.rpc().invoke(
+ handler="get_connection_tracker_info",
+ params='"table: inbound"').validate_result_with_text('{"inbound":
{"count": "0", "list": []}}')
+# requets outbound only
+traffic_ctl.rpc().invoke(
+ handler="get_connection_tracker_info",
+ params='"table: outbound"').validate_result_with_text('{"outbound":
{"count": "0", "list": []}}')
diff --git a/tests/gold_tests/traffic_ctl/traffic_ctl_test_utils.py
b/tests/gold_tests/traffic_ctl/traffic_ctl_test_utils.py
index c8a2d57576..624e8e1f5f 100644
--- a/tests/gold_tests/traffic_ctl/traffic_ctl_test_utils.py
+++ b/tests/gold_tests/traffic_ctl/traffic_ctl_test_utils.py
@@ -150,6 +150,42 @@ class Server():
self.__finish()
+class RPC():
+ """
+ Handy class to map traffic_ctl server options.
+ """
+
+ def __init__(self, dir, tr, tn):
+ self._cmd = "traffic_ctl rpc "
+ self._tr = tr
+ self._dir = dir
+ self._tn = tn
+
+ def invoke(self, handler: str, params={}):
+ if not params:
+ self._cmd = f'{self._cmd} invoke {handler} -f json'
+ else:
+ self._cmd = f'{self._cmd} invoke {handler} -p {str(params)} -f
json'
+
+ return self
+
+ def __finish(self):
+ """
+ Sets the command to the test. Make sure this gets called after
+ validation is set. Without this call the test will fail.
+ """
+ self._tr.Processes.Default.Command = self._cmd
+
+ def validate_with_text(self, text: str):
+ self._tr.Processes.Default.Streams.stdout = MakeGoldFileWithText(text,
self._dir, self._tn)
+ self.__finish()
+
+ def validate_result_with_text(self, text: str):
+ full_text = f'{{\"jsonrpc\": \"2.0\", \"result\": {text}, \"id\":
{"``"}}}'
+ self._tr.Processes.Default.Streams.stdout =
MakeGoldFileWithText(full_text, self._dir, self._tn)
+ self.__finish()
+
+
'''
Handy wrapper around traffic_ctl, ATS and the autest output validation
mechanism.
@@ -213,6 +249,10 @@ class TrafficCtl(Config, Server):
self.add_test()
return Server(self._Test.TestDirectory,
self._tests[self.__get_index()], self._testNumber)
+ def rpc(self):
+ self.add_test()
+ return RPC(self._Test.TestDirectory, self._tests[self.__get_index()],
self._testNumber)
+
def Make_traffic_ctl(test, records_yaml=None):
tctl = TrafficCtl(test, records_yaml)