diff options
| author | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-08-02 04:13:11 +0300 |
|---|---|---|
| committer | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-08-02 04:51:09 +0300 |
| commit | edd9f9ce6fc0f99e751fde20b0bb55bbd6b3a2f9 (patch) | |
| tree | 3f905c80bdf306ec08c1b93484f7b7e3764e4bde /tests | |
| parent | 93c6a21673f693640ec8004b063a3471f784f8fa (diff) | |
| download | accel-ppp-edd9f9ce6fc0f99e751fde20b0bb55bbd6b3a2f9.tar.gz accel-ppp-edd9f9ce6fc0f99e751fde20b0bb55bbd6b3a2f9.zip | |
metrics: expose session details in JSON output
Add an opt-in sessions setting for the JSON metrics renderer. Include session identity, addressing, protocol state, interface context, uptime, and traffic counters while keeping Prometheus output aggregate-only.
The session list is walked with ses_lock held, so report the accounting counters the session last sampled rather than calling ap_session_read_stats(): that issues a synchronous netlink round trip per session, which would stall session setup and teardown for the duration of a scrape, it writes back into the session while only the read lock is held, and it needs the thread local net of the session's namespace, which the metrics context does not have. Counter freshness therefore follows accounting, which the documentation spells out.
Escape malformed UTF-8 in peer supplied strings so a single bad username cannot make the whole document undecodable, and reserve room for the response header in front of the rendered body so a body that can be megabytes is not copied a second time.
Document the privacy-sensitive option in both accel-ppp.conf and the man page, and cover the empty session list, the aggregate-only Prometheus output and the response framing in the metrics integration test.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/accel-pppd/general/test_metrics.py | 29 |
1 files changed, 25 insertions, 4 deletions
diff --git a/tests/accel-pppd/general/test_metrics.py b/tests/accel-pppd/general/test_metrics.py index 6344d3d0..aa90a13a 100644 --- a/tests/accel-pppd/general/test_metrics.py +++ b/tests/accel-pppd/general/test_metrics.py @@ -7,7 +7,7 @@ import pytest PROM_PORT = 9099 -def _config(fmt): +def _config(fmt, sessions=False): return f""" [modules] metrics @@ -25,6 +25,7 @@ def _config(fmt): [metrics] address=127.0.0.1:{PROM_PORT} format={fmt} + sessions={int(sessions)} """ @@ -33,7 +34,8 @@ def _request(path, method="GET"): try: conn.request(method, path) resp = conn.getresponse() - body = resp.read().decode("utf-8", "replace") + # strict: the renderer must never emit a body that is not valid UTF-8 + body = resp.read().decode("utf-8") headers = {k.lower(): v for k, v in resp.getheaders()} return resp.status, headers, body finally: @@ -43,7 +45,8 @@ def _request(path, method="GET"): class TestPrometheus: @pytest.fixture() def accel_pppd_config(self): - return _config("prometheus") + # sessions=1 must stay a no-op here: prometheus output is aggregate only + return _config("prometheus", sessions=True) def test_metrics_prometheus(self, accel_pppd_instance): assert accel_pppd_instance @@ -55,6 +58,7 @@ class TestPrometheus: assert "accel_ppp_build_info{version=" in body assert "# TYPE accel_ppp_uptime_seconds gauge" in body assert 'accel_ppp_sessions{state="active"}' in body + assert "session_details" not in body def test_metrics_404_unknown_path(self, accel_pppd_instance): assert accel_pppd_instance @@ -74,7 +78,7 @@ class TestPrometheus: class TestJson: @pytest.fixture() def accel_pppd_config(self): - return _config("json") + return _config("json", sessions=True) def test_metrics_json(self, accel_pppd_instance): assert accel_pppd_instance @@ -84,8 +88,25 @@ class TestJson: assert status == 200 assert headers.get("content-type") == "application/json" + assert int(headers["content-length"]) == len(body.encode("utf-8")) + doc = json.loads(body) assert "build" in doc and "version" in doc["build"] assert "uptime_seconds" in doc assert "active" in doc["sessions"] assert "threads" in doc["core"] + assert doc["session_details"] == [] + + +class TestJsonNoSessions: + @pytest.fixture() + def accel_pppd_config(self): + return _config("json") + + def test_metrics_json_without_sessions(self, accel_pppd_instance): + assert accel_pppd_instance + + status, _, body = _request("/metrics") + + assert status == 200 + assert "session_details" not in json.loads(body) |
