feat(playout): rewrite stats collector (#2028)

- Replace defusedxml with lxml
This commit is contained in:
Jonas L 2022-08-09 21:14:19 +02:00 committed by GitHub
parent 02c16de2ab
commit 4019367abc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 426 additions and 163 deletions

View file

6
playout/tests/fixtures/__init__.py vendored Normal file
View file

@ -0,0 +1,6 @@
from pathlib import Path
fixture_path = Path(__file__).parent
icecast_stats = fixture_path / "icecast_stats.xml"
shoutcast_admin = fixture_path / "shoutcast_admin.xml"

View file

@ -0,0 +1,74 @@
<?xml version="1.0"?>
<icestats>
<admin>icemaster@radio.org</admin>
<client_connections>3935</client_connections>
<clients>7</clients>
<connections>4201</connections>
<file_connections>14</file_connections>
<host>localhost</host>
<listener_connections>117</listener_connections>
<listeners>5</listeners>
<location>Moon</location>
<server_id>Icecast 2.4.4</server_id>
<server_start>Tue, 15 Mar 2022 18:29:12 +0100</server_start>
<server_start_iso8601>2022-03-15T18:29:12+0100</server_start_iso8601>
<source_client_connections>2</source_client_connections>
<source_relay_connections>0</source_relay_connections>
<source_total_connections>2</source_total_connections>
<sources>2</sources>
<stats>0</stats>
<stats_connections>0</stats_connections>
<source mount="/main.mp3">
<audio_info>channels=2;samplerate=44100;bitrate=320</audio_info>
<bitrate>320</bitrate>
<channels>2</channels>
<genre>various</genre>
<listener_peak>7</listener_peak>
<listeners>3</listeners>
<listenurl>http://localhost:8800/main.mp3</listenurl>
<max_listeners>unlimited</max_listeners>
<public>1</public>
<samplerate>44100</samplerate>
<server_description>Main (mp3 320kbps)</server_description>
<server_name>Radio</server_name>
<server_type>audio/mpeg</server_type>
<server_url>https://www.radio.org</server_url>
<slow_listeners>2</slow_listeners>
<source_ip>192.168.100.20</source_ip>
<stream_start>Tue, 15 Mar 2022 18:29:19 +0100</stream_start>
<stream_start_iso8601>2022-03-15T18:29:19+0100</stream_start_iso8601>
<title>Robert Glasper Experiment/Lupe Fiasco/Bilal - Always Shine</title>
<total_bytes_read>6110388200</total_bytes_read>
<total_bytes_sent>20338244727</total_bytes_sent>
<user_agent>Liquidsoap/1.4.4 (Unix; OCaml 4.10.0)</user_agent>
</source>
<source mount="/main.ogg">
<audio_bitrate>256000</audio_bitrate>
<audio_channels>2</audio_channels>
<audio_info>channels=2;quality=0.8;samplerate=44100</audio_info>
<audio_samplerate>44100</audio_samplerate>
<channels>2</channels>
<genre>various</genre>
<ice-bitrate>256</ice-bitrate>
<listener_peak>4</listener_peak>
<listeners>2</listeners>
<listenurl>http://localhost:8800/main.ogg</listenurl>
<max_listeners>unlimited</max_listeners>
<public>1</public>
<quality>0.8</quality>
<samplerate>44100</samplerate>
<server_description>Main (ogg 256kbps)</server_description>
<server_name>Radio</server_name>
<server_type>application/ogg</server_type>
<server_url>https://www.radio.org</server_url>
<slow_listeners>2</slow_listeners>
<source_ip>192.168.100.20</source_ip>
<stream_start>Tue, 15 Mar 2022 18:29:19 +0100</stream_start>
<stream_start_iso8601>2022-03-15T18:29:19+0100</stream_start_iso8601>
<subtype>Vorbis</subtype>
<title>Robert Glasper Experiment/Lupe Fiasco/Bilal - Always Shine</title>
<total_bytes_read>4499297657</total_bytes_read>
<total_bytes_sent>9051758982</total_bytes_sent>
<user_agent>Liquidsoap/1.4.4 (Unix; OCaml 4.10.0)</user_agent>
</source>
</icestats>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SHOUTCASTSERVER>
<CURRENTLISTENERS>1</CURRENTLISTENERS>
<PEAKLISTENERS>0</PEAKLISTENERS>
<MAXLISTENERS>32</MAXLISTENERS>
<UNIQUELISTENERS>0</UNIQUELISTENERS>
<AVERAGETIME>0</AVERAGETIME>
</SHOUTCASTSERVER>

View file

View file

@ -0,0 +1,140 @@
from datetime import datetime
from unittest.mock import Mock, call
import pytest
from libretime_playout.history.stats import Server, Source, Stats, StatsCollector
from ..fixtures import icecast_stats, shoutcast_admin
@pytest.fixture(name="server")
def _server_fixture():
return Server(
host="example.com",
port=8000,
auth=("admin", "hackme"),
sources=[
Source("s1", "main.ogg"),
],
)
def test_stats_collector_collect_server_stats(requests_mock, server):
requests_mock.get(
"http://example.com:8000/admin/stats.xml",
content=icecast_stats.read_bytes(),
)
legacy_client = Mock()
collector = StatsCollector(legacy_client)
assert collector.collect_server_stats(server) == {"main.ogg": Stats(listeners=2)}
legacy_client.assert_not_called()
def test_stats_collector_collect_server_stats_unauthorized(requests_mock, server):
requests_mock.get(
"http://example.com:8000/admin/stats.xml",
status_code=401,
)
legacy_client = Mock()
collector = StatsCollector(legacy_client)
assert not collector.collect_server_stats(server)
legacy_client.assert_has_calls(
[
call.update_stream_setting_table(
{
"s1": "401 Client Error: None for url: http://example.com:8000/admin/stats.xml",
}
)
]
)
def test_stats_collector_collect_server_stats_invalid_xml(requests_mock, server):
requests_mock.get(
"http://example.com:8000/admin/stats.xml",
content=b"""<?xml version="1.0"?>
<icestats>
<host>localhost
</icestats>
""",
)
legacy_client = Mock()
collector = StatsCollector(legacy_client)
assert not collector.collect_server_stats(server)
legacy_client.assert_has_calls(
[
call.update_stream_setting_table(
{
"s1": "Opening and ending tag mismatch: host line 3 and icestats, line 4, column 12 (<string>, line 4)",
}
)
]
)
def test_stats_collector_collect(requests_mock):
requests_mock.get(
"http://example.com:8000/admin/stats.xml",
content=icecast_stats.read_bytes(),
)
requests_mock.get(
"http://shoutcast.com:8000/admin.cgi?sid=1&mode=viewxml",
content=shoutcast_admin.read_bytes(),
)
legacy_client = Mock()
default_stream = {
"enable": "true",
"output": "icecast",
"host": "example.com",
"port": 8000,
"mount": "main.ogg",
"admin_user": "admin",
"admin_pass": "hackme",
}
legacy_client.get_stream_parameters.return_value = {
"stream_params": {
"s1": {**default_stream},
"s2": {**default_stream, "enable": "false", "mount": "main.mp3"},
"s3": {**default_stream, "mount": "unknown.mp3"},
"s4": {
**default_stream,
"output": "shoutcast",
"host": "shoutcast.com",
"mount": "shout.mp3",
},
}
}
collector = StatsCollector(legacy_client)
collector.collect(_timestamp=datetime(2022, 8, 9, 11, 19, 7))
legacy_client.assert_has_calls(
[
call.get_stream_parameters(),
call.push_stream_stats(
[
{
"timestamp": "2022-08-09 11:19:07",
"num_listeners": 2,
"mount_name": "main.ogg",
},
{
"timestamp": "2022-08-09 11:19:07",
"num_listeners": 1,
"mount_name": "shoutcast",
},
]
),
]
)