Skip to content

Commit 45b7a4b

Browse files
committed
Refactor and prepare working scenario with full compliance check
1 parent 5e17b76 commit 45b7a4b

File tree

9 files changed

+178
-245
lines changed

9 files changed

+178
-245
lines changed

tests/validation/compliance/pcap_compliance.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,25 +82,27 @@ def upload_pcap(self):
8282
print(f"Extracted UUID: >>>{self.pcap_id}<<<")
8383
return self.pcap_id
8484

85-
def download_report(self, test_name):
85+
def download_report(self, test_name=None):
8686
"""
8787
Download the compliance report for the uploaded PCAP file.
8888
Saves the report as a JSON file in a timestamped directory.
8989
Returns the path to the saved report.
9090
"""
91-
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
92-
self.report_dir = os.path.join("reports", test_name, timestamp)
93-
os.makedirs(self.report_dir, exist_ok=True)
9491
url = f"http://{self.ebu_ip}/api/pcap/{self.pcap_id}/report?type=json"
9592
headers = {"Authorization": f"Bearer {self.token}"}
9693
response = self.session.get(
9794
url, headers=headers, verify=False, proxies=self.proxies
9895
)
9996
response.raise_for_status()
100-
report_path = os.path.join(self.report_dir, f"{self.pcap_id}.json")
101-
with open(report_path, "w") as f:
102-
json.dump(response.json(), f, indent=2)
103-
return report_path
97+
if test_name is not None:
98+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
99+
self.report_dir = os.path.join("reports", test_name, timestamp)
100+
os.makedirs(self.report_dir, exist_ok=True)
101+
report_path = os.path.join(self.report_dir, f"{self.pcap_id}.json")
102+
with open(report_path, "w") as f:
103+
json.dump(response.json(), f, indent=2)
104+
return report_path
105+
return response.json()
104106

105107
def check_compliance(self, report_path):
106108
"""

tests/validation/compliance/upload_pcap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
if __name__ == "__main__":
55
from pcap_compliance import PcapComplianceClient
66
else:
7-
from .pcap_compliance import PcapComplianceClient
7+
from compliance.pcap_compliance import PcapComplianceClient
88

99

1010
def parse_args():

tests/validation/conftest.py

Lines changed: 82 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,20 @@
1212
import pytest
1313
from common.mtl_manager.mtlManager import MtlManager
1414
from common.nicctl import Nicctl
15-
from create_pcap_file.netsniff import NetsniffRecorder
16-
from create_pcap_file.tcpdump import TcpDumpRecorder
15+
from compliance.pcap_compliance import PcapComplianceClient
16+
from create_pcap_file.netsniff import NetsniffRecorder, calculate_packets_per_frame
1717
from mfd_common_libs.custom_logger import add_logging_level
1818
from mfd_common_libs.log_levels import TEST_FAIL, TEST_INFO, TEST_PASS
1919
from mfd_connect.exceptions import ConnectionCalledProcessError
20-
from mtl_engine.const import LOG_FOLDER, TESTCMD_LVL
20+
from mtl_engine.const import DOWNLOAD_REPORT_TRIES, FRAMES_CAPTURE, LOG_FOLDER, TESTCMD_LVL
2121
from mtl_engine.csv_report import (
2222
csv_add_test,
2323
csv_write_report,
2424
update_compliance_result,
2525
)
2626

2727
# FIXME: Perhaps, it could be set less statically
28+
from mtl_engine.execute import log_fail
2829
from mtl_engine.ffmpeg_app import ip_dict, ip_dict_rgb24_multiple
2930
from mtl_engine.ramdisk import Ramdisk
3031
from mtl_engine.stash import (
@@ -38,6 +39,7 @@
3839
)
3940
from pytest_mfd_logging.amber_log_formatter import AmberLogFormatter
4041

42+
4143
logger = logging.getLogger(__name__)
4244
phase_report_key = pytest.StashKey[Dict[str, pytest.CollectReport]]()
4345

@@ -273,126 +275,90 @@ def log_session():
273275
if os.path.exists("pytest.log"):
274276
shutil.copy("pytest.log", f"{LOG_FOLDER}/latest/pytest.log")
275277
else:
276-
logging.warning("pytest.log not found, skipping copy")
278+
logger.warning("pytest.log not found, skipping copy")
277279
csv_write_report(f"{LOG_FOLDER}/latest/report.csv")
278280

279281

280-
def read_ebu_creds(config_path):
281-
# Load EBU IP and credentials from YAML config
282-
import yaml
283-
284-
if config_path:
285-
with open(config_path, "r") as f:
286-
config = yaml.safe_load(f)
287-
instance = config["instances"]
288-
ebu_ip = instance.get("name", None)
289-
user = instance.get("username", None)
290-
password = instance.get("password", None)
291-
proxy = instance.get("proxy", None)
292-
return ebu_ip, user, password, proxy
293-
return None, None, None, None
294-
295-
296-
@pytest.fixture(scope="function", autouse=False)
297-
def pcap_capture(
298-
test_config, hosts, nic_port_list, video_format, output_format, mtl_path
282+
@pytest.fixture(scope="function")
283+
def pcap_capture(request, media_file, test_config, hosts, mtl_path
299284
):
300285
capture_cfg = test_config.get("capture_cfg", {})
301-
capture_cfg["test_name"] = (
302-
f"test_rx_ffmpeg_tx_ffmpeg_dual_{video_format}_{output_format}"
303-
)
304-
305-
host = hosts["client"] if "client" in hosts else list(hosts.values())[0]
306-
# FIXME: If possible, change this not to be hardcoded
307-
src_ip = ip_dict["tx_interfaces"]
308-
dst_ip = ip_dict_rgb24_multiple["p_tx_ip_2"]
309-
310286
capturer = None
311287
if capture_cfg and capture_cfg.get("enable"):
312-
if capture_cfg.get("tool") == "tcpdump":
313-
capturer = TcpDumpRecorder(
314-
host=host,
315-
test_name=capture_cfg.get("test_name", "capture"),
316-
pcap_dir=capture_cfg.get("pcap_dir", "/tmp"),
317-
interface=capture_cfg.get("interface"),
288+
host = hosts["client"] if "client" in hosts else list(hosts.values())[0]
289+
media_file_info, _ = media_file
290+
test_name = request.node.name
291+
if "frames_number" not in capture_cfg and "capture_time" not in capture_cfg:
292+
capture_cfg["packets_number"] = FRAMES_CAPTURE * calculate_packets_per_frame(
293+
media_file_info["width"],
294+
media_file_info["height"]
318295
)
319-
elif capture_cfg.get("tool") in ["netsniff", "netsniff-ng"]:
320-
# Filtering
321-
capture_filter = ""
322-
# TODO: Enable filtering back when checked
323-
if src_ip:
324-
capture_filter += f"src {src_ip}"
325-
if dst_ip and not capture_filter == "":
326-
capture_filter += f" and dst {dst_ip}"
327-
elif dst_ip:
328-
capture_filter += f"dst {dst_ip}"
329-
# Class prep
330-
capturer = NetsniffRecorder(
331-
host=host,
332-
test_name=capture_cfg.get("test_name", "capture"),
333-
pcap_dir=capture_cfg.get("pcap_dir", "/tmp"),
334-
interface=capture_cfg.get("interface", "eth0"),
335-
capture_filter=(
336-
capture_filter if capture_filter != "" else None
337-
), # Avoid forcing an empty filter
296+
logger.info(f"Capture {capture_cfg['packets_number']} packets for {FRAMES_CAPTURE} frames")
297+
elif "frames_number" in capture_cfg:
298+
capture_cfg["packets_number"] = capture_cfg.pop("frames_number") * calculate_packets_per_frame(
299+
media_file_info["width"],
300+
media_file_info["height"]
338301
)
339-
else:
340-
logging.error(f"Unknown capture tool {capture_cfg.get('tool')}")
341-
if capturer:
342-
if isinstance(capturer, TcpDumpRecorder):
343-
# TODO: Perhaps it would need to be changed to use .start() instead of .capture()
344-
capturer.capture(capture_time=capture_cfg.get("time", test_time))
345-
elif isinstance(capturer, NetsniffRecorder):
346-
capturer.start()
347-
else:
348-
logging.error(f"Unknown capturer class {capturer.__class__.__name__}")
302+
logger.info(f"Capture {capture_cfg['packets_number']} packets for {capture_cfg['frames_number']} frames")
303+
capturer = NetsniffRecorder(
304+
host=host,
305+
test_name=test_name,
306+
pcap_dir=capture_cfg.get("pcap_dir", "/tmp"),
307+
interface=host.network_interfaces[0].name,
308+
silent=capture_cfg.get("silent", True),
309+
packets_capture=capture_cfg.get("packets_number", None),
310+
capture_time=capture_cfg.get("capture_time", None)
311+
)
349312
yield capturer
350313
if capturer:
351-
capturer.stop()
352-
if capture_cfg and capture_cfg.get("compliance", False):
353-
# FIXME: This is generally a bad practice to call it like that, but for now it is the easiest way
354-
ebu_ip, ebu_login, ebu_passwd, ebu_proxy = read_ebu_creds(
355-
config_path=capture_cfg.get("ebu_yaml_path", "configs/ebu_list.yaml")
356-
) # Reads from executor
357-
proxy_cmd = f" --proxy {ebu_proxy}" if ebu_proxy else ""
358-
compliance_upl = host.connection.execute_command(
359-
"python3 ./tests/validation/compliance/upload_pcap.py"
360-
f" --ip {ebu_ip}"
361-
f" --login {ebu_login}"
362-
f" --password {ebu_passwd}"
363-
f" --pcap_file_path {capturer.pcap_file}{proxy_cmd}",
364-
cwd=f"{str(mtl_path)}",
365-
)
366-
if compliance_upl.return_code != 0:
367-
logging.error(f"PCAP upload failed: {compliance_upl.stderr}")
368-
else:
369-
uuid = (
370-
compliance_upl.stdout.split(">>>UUID>>>")[1]
371-
.split("<<<UUID<<<")[0]
372-
.strip()
373-
)
374-
logging.debug(
375-
f"PCAP successfully uploaded to EBU LIST with UUID: {uuid}"
376-
)
377-
378-
379-
@pytest.fixture(scope="session", autouse=True)
380-
def compliance_report(request, log_session, test_config):
381-
"""
382-
This function is used for compliance check and report.
383-
"""
384-
# TODO: Implement compliance check logic. When tcpdump pcap is enabled, at the end of the test session all pcaps
385-
# shall be send into EBU list.
386-
# Pcaps shall be stored in the ramdisk, and then moved to the compliance
387-
# folder or send into EBU list after each test finished and remove it from the ramdisk.
388-
# Compliance report generation logic goes here after yield. Or in another class / function but triggered here.
389-
# AFAIK names of pcaps contains test name so it can be matched with result of each test like in code below.
390-
yield
391-
if test_config.get("compliance", False):
392-
logging.info("Compliance mode enabled, updating compliance results")
393-
for item in request.session.items:
394-
test_case = item.nodeid
395-
update_compliance_result(test_case, "Fail")
314+
ebu_server = test_config.get("ebu_server", {})
315+
if not ebu_server:
316+
logger.error("EBU server configuration not found in test_config.yaml")
317+
return
318+
ebu_ip = ebu_server.get("name", None)
319+
ebu_login = ebu_server.get("username", None)
320+
ebu_passwd = ebu_server.get("password", None)
321+
ebu_proxy = ebu_server.get("proxy", None)
322+
proxy_cmd = f" --proxy {ebu_proxy}" if ebu_proxy else ""
323+
compliance_upl = host.connection.execute_command(
324+
"python3 ./tests/validation/compliance/upload_pcap.py"
325+
f" --ip {ebu_ip}"
326+
f" --login {ebu_login}"
327+
f" --password {ebu_passwd}"
328+
f" --pcap_file_path {capturer.pcap_file}{proxy_cmd}",
329+
cwd=f"{str(mtl_path)}",
330+
)
331+
if compliance_upl.return_code != 0:
332+
logger.error(f"PCAP upload failed: {compliance_upl.stderr}")
333+
return
334+
uuid = (
335+
compliance_upl.stdout.split(">>>UUID>>>")[1]
336+
.split("<<<UUID<<<")[0]
337+
.strip()
338+
)
339+
logger.debug(
340+
f"PCAP successfully uploaded to EBU LIST with UUID: {uuid}"
341+
)
342+
uploader = PcapComplianceClient(config_path="")
343+
uploader.ebu_ip = ebu_ip
344+
uploader.user = ebu_login
345+
uploader.password = ebu_passwd
346+
uploader.pcap_id = uuid
347+
uploader.proxies = {"http": ebu_proxy, "https": ebu_proxy}
348+
uploader.authenticate() # Authenticate with the EBU server
349+
report = {"analyzed": False}
350+
tries = DOWNLOAD_REPORT_TRIES
351+
while (not report.get("analyzed", False)) and tries > 0:
352+
time.sleep(1)
353+
tries -= 1
354+
report = uploader.download_report()
355+
if not report.get("analyzed", False):
356+
logger.error(f"Report is not ready after {DOWNLOAD_REPORT_TRIES} seconds, skipping compliance check")
357+
return
358+
if report.get("not_compliant_streams", 1) == 0:
359+
logger.info("PCAP compliance check passed")
360+
else:
361+
log_fail("PCAP compliance check failed")
396362

397363

398364
@pytest.fixture(scope="function", autouse=True)
@@ -415,19 +381,19 @@ def log_case(request, caplog: pytest.LogCaptureFixture):
415381
yield
416382
report = request.node.stash[phase_report_key]
417383
if report["setup"].failed:
418-
logging.log(level=TEST_FAIL, msg=f"Setup failed for {case_id}")
384+
logger.log(level=TEST_FAIL, msg=f"Setup failed for {case_id}")
419385
os.chmod(logfile, 0o4755)
420386
result = "Fail"
421387
elif ("call" not in report) or report["call"].failed:
422-
logging.log(level=TEST_FAIL, msg=f"Test failed for {case_id}")
388+
logger.log(level=TEST_FAIL, msg=f"Test failed for {case_id}")
423389
os.chmod(logfile, 0o4755)
424390
result = "Fail"
425391
elif report["call"].passed:
426-
logging.log(level=TEST_PASS, msg=f"Test passed for {case_id}")
392+
logger.log(level=TEST_PASS, msg=f"Test passed for {case_id}")
427393
os.chmod(logfile, 0o755)
428394
result = "Pass"
429395
else:
430-
logging.log(level=TEST_INFO, msg=f"Test skipped for {case_id}")
396+
logger.log(level=TEST_INFO, msg=f"Test skipped for {case_id}")
431397
result = "Skip"
432398

433399
logger.removeHandler(fh)

0 commit comments

Comments
 (0)