Skip to content

Commit ab25c40

Browse files
authored
jsonrpc - Implement handler to fetch connection tracker information. (#12260)
This pr implements the rpc handler, and some unit tests to validate basic output.
1 parent fc4fa5e commit ab25c40

6 files changed

Lines changed: 224 additions & 1 deletion

File tree

doc/developer-guide/jsonrpc/jsonrpc-api.en.rst

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,8 @@ JSONRPC API
469469

470470
* `filemanager.get_files_registry`_
471471

472+
* `get_connection_tracker_info`_
473+
472474
.. _jsonapi-management-records:
473475

474476

@@ -1667,6 +1669,88 @@ Response:
16671669
}
16681670
}
16691671
1672+
.. _get_connection_tracker_info:
1673+
1674+
get_connection_tracker_info
1675+
----------------------------
1676+
1677+
|method|
1678+
1679+
Description
1680+
~~~~~~~~~~~
1681+
1682+
Get inbound and outbound connection tracking data from |TS|.
1683+
1684+
Parameters
1685+
~~~~~~~~~~
1686+
1687+
======================= ============= ==================================================================================
1688+
Field Type Description
1689+
======================= ============= ==================================================================================
1690+
``table`` |str| Specifies which group table to query, ``inbound`` or ``outbound``. If value is
1691+
``both`` then both tables will be added to the response. The default is ``outbound``.
1692+
======================= ============= ==================================================================================
1693+
1694+
You can query this api using `invoke` functionality from :ref:`traffic_ctl_rpc`.
1695+
1696+
.. code-block:: bash
1697+
:linenos:
1698+
1699+
$ traffic_ctl rpc invoke get_connection_tracker_info -p 'table: both' -f json
1700+
1701+
1702+
.. note::
1703+
1704+
JSON output can be formatted by any tool like `jq`.
1705+
1706+
Result
1707+
~~~~~~
1708+
1709+
The response will contain inbound/outbound info or an error. :ref:`jsonrpc-node-errors`.
1710+
1711+
Response examples
1712+
~~~~~~~~~~~~~~~~~
1713+
1714+
.. code-block:: json
1715+
:linenos:
1716+
1717+
{
1718+
"id":"f4477ac4-0d44-11eb-958d-001fc69cc946",
1719+
"jsonrpc":"2.0",
1720+
"result":{
1721+
"outbound":{
1722+
"count":"1",
1723+
"list":[
1724+
{
1725+
"type":"both",
1726+
"ip":"127.0.0.1:80",
1727+
"fqdn":"127.0.0.1",
1728+
"current":"8",
1729+
"max":"8",
1730+
"blocked":"0",
1731+
"alert":"0"
1732+
}
1733+
]
1734+
},
1735+
"inbound":{
1736+
"count":"1",
1737+
"list":[
1738+
{
1739+
"type":"ip",
1740+
"ip":"127.0.0.1:34226",
1741+
"fqdn":"",
1742+
"current":"2",
1743+
"max":"0",
1744+
"blocked":"0",
1745+
"alert":"0"
1746+
}
1747+
]
1748+
}
1749+
}
1750+
}
1751+
1752+
1753+
16701754
See also
16711755
========
16721756

include/mgmt/rpc/handlers/server/Server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ swoc::Rv<YAML::Node> server_start_drain(std::string_view const &id, YAML::Node c
2828
swoc::Rv<YAML::Node> server_stop_drain(std::string_view const &id, YAML::Node const &);
2929
void server_shutdown(YAML::Node const &);
3030
swoc::Rv<YAML::Node> get_server_status(std::string_view const &id, YAML::Node const &);
31+
swoc::Rv<YAML::Node> get_connection_tracker_info(std::string_view const &id, YAML::Node const &params);
3132

3233
} // namespace rpc::handlers::server

src/mgmt/rpc/handlers/server/Server.cc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020

2121
#include "../../../../iocore/cache/P_CacheDir.h"
22+
#include "iocore/net/ConnectionTracker.h"
2223
#include "mgmt/rpc/handlers/server/Server.h"
2324
#include "mgmt/rpc/handlers/common/ErrorUtils.h"
2425
#include "mgmt/rpc/handlers/common/Utils.h"
@@ -42,6 +43,33 @@ namespace field_names
4243
struct DrainInfo {
4344
bool noNewConnections{false};
4445
};
46+
47+
struct ConnectionTrackingInfo {
48+
enum TableFlags {
49+
NOT_SET = 0,
50+
INBOUND = 1 << 0, // Inbound table
51+
OUTBOUND = 1 << 1 // Outbound table
52+
};
53+
TableFlags table{TableFlags::OUTBOUND}; // default.
54+
};
55+
using TFLags = ConnectionTrackingInfo::TableFlags;
56+
constexpr TFLags
57+
operator|(const TFLags rhs, const TFLags lhs)
58+
{
59+
return static_cast<TFLags>(static_cast<uint32_t>(rhs) | static_cast<uint32_t>(lhs));
60+
}
61+
62+
constexpr TFLags &
63+
operator|=(TFLags &rhs, TFLags lhs)
64+
{
65+
return rhs = rhs | lhs;
66+
}
67+
68+
constexpr TFLags
69+
operator&(TFLags rhs, TFLags lhs)
70+
{
71+
return static_cast<TFLags>(static_cast<uint32_t>(rhs) & static_cast<uint32_t>(lhs));
72+
}
4573
} // namespace rpc::handlers::server
4674
namespace YAML
4775
{
@@ -61,6 +89,31 @@ template <> struct convert<rpc::handlers::server::DrainInfo> {
6189
return true;
6290
}
6391
};
92+
template <> struct convert<rpc::handlers::server::ConnectionTrackingInfo> {
93+
static constexpr auto table{"table"};
94+
static bool
95+
decode(const Node &node, rpc::handlers::server::ConnectionTrackingInfo &rhs)
96+
{
97+
namespace field = rpc::handlers::server::field_names;
98+
if (!node.IsMap()) {
99+
return false;
100+
}
101+
// optional
102+
if (auto n = node[table]; !n.IsNull()) {
103+
auto const &val = n.as<std::string>();
104+
if (val == "both") {
105+
rhs.table = rpc::handlers::server::TFLags::INBOUND | rpc::handlers::server::TFLags::OUTBOUND;
106+
} else if (val == "inbound") {
107+
rhs.table = rpc::handlers::server::TFLags::INBOUND;
108+
} else if (val == "outbound") {
109+
rhs.table = rpc::handlers::server::TFLags::OUTBOUND;
110+
} else {
111+
throw std::runtime_error("Invalid table type. Use [both|inbound|outbound]");
112+
}
113+
}
114+
return true;
115+
}
116+
};
64117
} // namespace YAML
65118

66119
namespace rpc::handlers::server
@@ -150,4 +203,32 @@ get_server_status(std::string_view const & /* params ATS_UNUSED */, YAML::Node c
150203
}
151204
return resp;
152205
}
206+
207+
swoc::Rv<YAML::Node>
208+
get_connection_tracker_info(std::string_view const & /* params ATS_UNUSED */, YAML::Node const &params)
209+
{
210+
swoc::Rv<YAML::Node> resp;
211+
try {
212+
ConnectionTrackingInfo p;
213+
if (!params.IsNull()) {
214+
p = params.as<ConnectionTrackingInfo>();
215+
}
216+
217+
if (p.table & TFLags::OUTBOUND) {
218+
std::string json{ConnectionTracker::outbound_to_json_string()};
219+
resp.result()["outbound"] = YAML::Load(json);
220+
}
221+
222+
if (p.table & TFLags::INBOUND) {
223+
std::string json{ConnectionTracker::inbound_to_json_string()};
224+
resp.result()["inbound"] = YAML::Load(json);
225+
}
226+
227+
} catch (std::exception const &ex) {
228+
resp.errata()
229+
.assign(std::error_code{errors::Codes::SERVER})
230+
.note("Error found when calling get_connection_tracker_info API: {}", ex.what());
231+
}
232+
return resp;
233+
}
153234
} // namespace rpc::handlers::server

src/traffic_server/RpcAdminPubHandlers.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ register_admin_jsonrpc_handlers()
5757
{{rpc::RESTRICTED_API}});
5858
rpc::add_method_handler("get_server_status", &get_server_status, &core_ats_rpc_service_provider_handle,
5959
{{rpc::NON_RESTRICTED_API}});
60-
60+
rpc::add_method_handler("get_connection_tracker_info", &get_connection_tracker_info, &core_ats_rpc_service_provider_handle,
61+
{{rpc::NON_RESTRICTED_API}});
6162
// storage
6263
using namespace rpc::handlers::storage;
6364
rpc::add_method_handler("admin_storage_set_device_offline", &set_storage_offline, &core_ats_rpc_service_provider_handle,

tests/gold_tests/traffic_ctl/traffic_ctl_server_output.test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,19 @@
4343
traffic_ctl.server().status().validate_with_text(
4444
'{"initialized_done": "true", "is_ssl_handshaking_stopped": "false", "is_draining": "true", "is_event_system_shut_down": "false"}'
4545
)
46+
47+
# Get basic and empty connection tracker info.
48+
traffic_ctl.rpc().invoke(
49+
handler="get_connection_tracker_info", params='"table: both"').validate_result_with_text(
50+
'{"outbound": {"count": "0", "list": []}, "inbound": {"count": "0", "list": []}}')
51+
# default = outbound only
52+
traffic_ctl.rpc().invoke(
53+
handler="get_connection_tracker_info").validate_result_with_text('{"outbound": {"count": "0", "list": []}}')
54+
# requets inbound oonly
55+
traffic_ctl.rpc().invoke(
56+
handler="get_connection_tracker_info",
57+
params='"table: inbound"').validate_result_with_text('{"inbound": {"count": "0", "list": []}}')
58+
# requets outbound only
59+
traffic_ctl.rpc().invoke(
60+
handler="get_connection_tracker_info",
61+
params='"table: outbound"').validate_result_with_text('{"outbound": {"count": "0", "list": []}}')

tests/gold_tests/traffic_ctl/traffic_ctl_test_utils.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,42 @@ def validate_with_text(self, text: str):
150150
self.__finish()
151151

152152

153+
class RPC():
154+
"""
155+
Handy class to map traffic_ctl server options.
156+
"""
157+
158+
def __init__(self, dir, tr, tn):
159+
self._cmd = "traffic_ctl rpc "
160+
self._tr = tr
161+
self._dir = dir
162+
self._tn = tn
163+
164+
def invoke(self, handler: str, params={}):
165+
if not params:
166+
self._cmd = f'{self._cmd} invoke {handler} -f json'
167+
else:
168+
self._cmd = f'{self._cmd} invoke {handler} -p {str(params)} -f json'
169+
170+
return self
171+
172+
def __finish(self):
173+
"""
174+
Sets the command to the test. Make sure this gets called after
175+
validation is set. Without this call the test will fail.
176+
"""
177+
self._tr.Processes.Default.Command = self._cmd
178+
179+
def validate_with_text(self, text: str):
180+
self._tr.Processes.Default.Streams.stdout = MakeGoldFileWithText(text, self._dir, self._tn)
181+
self.__finish()
182+
183+
def validate_result_with_text(self, text: str):
184+
full_text = f'{{\"jsonrpc\": \"2.0\", \"result\": {text}, \"id\": {"``"}}}'
185+
self._tr.Processes.Default.Streams.stdout = MakeGoldFileWithText(full_text, self._dir, self._tn)
186+
self.__finish()
187+
188+
153189
'''
154190
155191
Handy wrapper around traffic_ctl, ATS and the autest output validation mechanism.
@@ -213,6 +249,10 @@ def server(self):
213249
self.add_test()
214250
return Server(self._Test.TestDirectory, self._tests[self.__get_index()], self._testNumber)
215251

252+
def rpc(self):
253+
self.add_test()
254+
return RPC(self._Test.TestDirectory, self._tests[self.__get_index()], self._testNumber)
255+
216256

217257
def Make_traffic_ctl(test, records_yaml=None):
218258
tctl = TrafficCtl(test, records_yaml)

0 commit comments

Comments
 (0)