Skip to content

Commit 7643980

Browse files
committed
Adjust ST20 and ST30 tests with netsniff
1 parent 45b7a4b commit 7643980

File tree

28 files changed

+216
-305
lines changed

28 files changed

+216
-305
lines changed

tests/validation/conftest.py

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@
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 DOWNLOAD_REPORT_TRIES, FRAMES_CAPTURE, LOG_FOLDER, TESTCMD_LVL
20+
from mtl_engine.const import (
21+
DOWNLOAD_REPORT_TRIES,
22+
FRAMES_CAPTURE,
23+
LOG_FOLDER,
24+
TESTCMD_LVL,
25+
)
2126
from mtl_engine.csv_report import (
2227
csv_add_test,
2328
csv_write_report,
29+
get_compliance_result,
2430
update_compliance_result,
2531
)
2632

@@ -39,7 +45,6 @@
3945
)
4046
from pytest_mfd_logging.amber_log_formatter import AmberLogFormatter
4147

42-
4348
logger = logging.getLogger(__name__)
4449
phase_report_key = pytest.StashKey[Dict[str, pytest.CollectReport]]()
4550

@@ -280,37 +285,38 @@ def log_session():
280285

281286

282287
@pytest.fixture(scope="function")
283-
def pcap_capture(request, media_file, test_config, hosts, mtl_path
284-
):
288+
def pcap_capture(request, media_file, test_config, hosts, mtl_path):
285289
capture_cfg = test_config.get("capture_cfg", {})
286290
capturer = None
287291
if capture_cfg and capture_cfg.get("enable"):
288292
host = hosts["client"] if "client" in hosts else list(hosts.values())[0]
289293
media_file_info, _ = media_file
290294
test_name = request.node.name
291295
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"]
296+
capture_cfg["packets_number"] = (
297+
FRAMES_CAPTURE * calculate_packets_per_frame(media_file_info)
298+
)
299+
logger.info(
300+
f"Capture {capture_cfg['packets_number']} packets for {FRAMES_CAPTURE} frames"
295301
)
296-
logger.info(f"Capture {capture_cfg['packets_number']} packets for {FRAMES_CAPTURE} frames")
297302
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"]
303+
capture_cfg["packets_number"] = capture_cfg[
304+
"frames_number"
305+
] * calculate_packets_per_frame(media_file_info)
306+
logger.info(
307+
f"Capture {capture_cfg['packets_number']} packets for {capture_cfg['frames_number']} frames"
301308
)
302-
logger.info(f"Capture {capture_cfg['packets_number']} packets for {capture_cfg['frames_number']} frames")
303309
capturer = NetsniffRecorder(
304310
host=host,
305311
test_name=test_name,
306312
pcap_dir=capture_cfg.get("pcap_dir", "/tmp"),
307313
interface=host.network_interfaces[0].name,
308314
silent=capture_cfg.get("silent", True),
309315
packets_capture=capture_cfg.get("packets_number", None),
310-
capture_time=capture_cfg.get("capture_time", None)
316+
capture_time=capture_cfg.get("capture_time", None),
311317
)
312318
yield capturer
313-
if capturer:
319+
if capturer and capturer.netsniff_process:
314320
ebu_server = test_config.get("ebu_server", {})
315321
if not ebu_server:
316322
logger.error("EBU server configuration not found in test_config.yaml")
@@ -320,7 +326,7 @@ def pcap_capture(request, media_file, test_config, hosts, mtl_path
320326
ebu_passwd = ebu_server.get("password", None)
321327
ebu_proxy = ebu_server.get("proxy", None)
322328
proxy_cmd = f" --proxy {ebu_proxy}" if ebu_proxy else ""
323-
compliance_upl = host.connection.execute_command(
329+
compliance_upl = capturer.host.connection.execute_command(
324330
"python3 ./tests/validation/compliance/upload_pcap.py"
325331
f" --ip {ebu_ip}"
326332
f" --login {ebu_login}"
@@ -332,13 +338,9 @@ def pcap_capture(request, media_file, test_config, hosts, mtl_path
332338
logger.error(f"PCAP upload failed: {compliance_upl.stderr}")
333339
return
334340
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+
compliance_upl.stdout.split(">>>UUID>>>")[1].split("<<<UUID<<<")[0].strip()
341342
)
343+
logger.debug(f"PCAP successfully uploaded to EBU LIST with UUID: {uuid}")
342344
uploader = PcapComplianceClient(config_path="")
343345
uploader.ebu_ip = ebu_ip
344346
uploader.user = ebu_login
@@ -353,9 +355,13 @@ def pcap_capture(request, media_file, test_config, hosts, mtl_path
353355
tries -= 1
354356
report = uploader.download_report()
355357
if not report.get("analyzed", False):
356-
logger.error(f"Report is not ready after {DOWNLOAD_REPORT_TRIES} seconds, skipping compliance check")
358+
logger.error(
359+
f"Report is not ready after {DOWNLOAD_REPORT_TRIES} seconds, skipping compliance check"
360+
)
357361
return
358-
if report.get("not_compliant_streams", 1) == 0:
362+
result = report.get("not_compliant_streams", 1)
363+
update_compliance_result(request.node.nodeid, "Pass" if result == 0 else "Fail")
364+
if result == 0:
359365
logger.info("PCAP compliance check passed")
360366
else:
361367
log_fail("PCAP compliance check failed")
@@ -380,18 +386,24 @@ def log_case(request, caplog: pytest.LogCaptureFixture):
380386
clear_issue()
381387
yield
382388
report = request.node.stash[phase_report_key]
383-
if report["setup"].failed:
384-
logger.log(level=TEST_FAIL, msg=f"Setup failed for {case_id}")
389+
390+
def fail_test(stage):
391+
logger.log(level=TEST_FAIL, msg=f"{stage} failed for {case_id}")
385392
os.chmod(logfile, 0o4755)
386-
result = "Fail"
393+
return "Fail"
394+
395+
if report["setup"].failed:
396+
result = fail_test("Setup")
387397
elif ("call" not in report) or report["call"].failed:
388-
logger.log(level=TEST_FAIL, msg=f"Test failed for {case_id}")
389-
os.chmod(logfile, 0o4755)
390-
result = "Fail"
398+
result = fail_test("Test")
391399
elif report["call"].passed:
392-
logger.log(level=TEST_PASS, msg=f"Test passed for {case_id}")
393-
os.chmod(logfile, 0o755)
394-
result = "Pass"
400+
compliance = get_compliance_result(case_id)
401+
if compliance is not None and compliance == "Fail":
402+
result = fail_test("Compliance")
403+
else:
404+
logger.log(level=TEST_PASS, msg=f"Test passed for {case_id}")
405+
os.chmod(logfile, 0o755)
406+
result = "Pass"
395407
else:
396408
logger.log(level=TEST_INFO, msg=f"Test skipped for {case_id}")
397409
result = "Skip"

tests/validation/create_pcap_file/netsniff.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,31 @@
22
# Copyright 2025 Intel Corporation
33
import logging
44
import os
5-
from datetime import datetime
65
from time import sleep
7-
from wsgiref import headers
86

9-
from mfd_connect.exceptions import ConnectionCalledProcessError, RemoteProcessInvalidState, RemoteProcessTimeoutExpired, SSHRemoteProcessEndException
7+
from mfd_connect.exceptions import (
8+
ConnectionCalledProcessError,
9+
RemoteProcessInvalidState,
10+
RemoteProcessTimeoutExpired,
11+
SSHRemoteProcessEndException,
12+
)
1013

1114
logger = logging.getLogger(__name__)
12-
1315
STARTUP_WAIT = 2 # Default wait time after starting the process
1416

1517

16-
def calculate_packets_per_frame(width: int, height: int, mtu: int = 1500) -> int:
18+
def calculate_packets_per_frame(media_file_info, mtu: int = 1500) -> int:
1719
# Simplified calculation for the number of packets per frame
18-
# Supported only 4:2:2 format
19-
pgroupsize = 5
20-
pgroupcoverage = 2
21-
headersize = 74 # Ethernet + IP + UDP + RTP headers
22-
packets = 1 + int((width * height) / (int((mtu - headersize) / pgroupsize) * pgroupcoverage))
20+
# Supported only 4:2:2 format or audio formats assuming 1 packet per 1ms
21+
packets = 1000
22+
if "width" in media_file_info and "height" in media_file_info:
23+
pgroupsize = 5
24+
pgroupcoverage = 2
25+
headersize = 74 # Ethernet + IP + UDP + RTP headers
26+
packets = 1 + int(
27+
(media_file_info["width"] * media_file_info["height"])
28+
/ (int((mtu - headersize) / pgroupsize) * pgroupcoverage)
29+
)
2330
return packets
2431

2532

@@ -77,7 +84,11 @@ def start(self):
7784
str(self.interface),
7885
"--out",
7986
self.pcap_file,
80-
f"--num {self.packets_capture}" if self.packets_capture is not None else "",
87+
(
88+
f"--num {self.packets_capture}"
89+
if self.packets_capture is not None
90+
else ""
91+
),
8192
f'-f "{self.capture_filter}"' if self.capture_filter else "",
8293
]
8394
logger.info(f"Running command: {' '.join(cmd)}")
@@ -114,12 +125,17 @@ def capture(self, startup_wait=2):
114125
logger.info("Capture complete.")
115126
else:
116127
try:
117-
logger.info(f"Capturing traffic for {self.packets_capture} packets...")
118-
self.netsniff_process.wait(timeout=startup_wait+10)
128+
logger.info(
129+
f"Capturing traffic for {self.packets_capture} packets..."
130+
)
131+
self.netsniff_process.wait(timeout=startup_wait + 10)
119132
logger.info("Capture complete.")
120133
logger.debug(self.netsniff_process.stdout_text)
121134
except RemoteProcessTimeoutExpired:
122-
logger.error("Capture timed out.")
135+
logger.warning(
136+
"Capture timed out. Probably not enough packets were sent. "
137+
"Please adjust packets_capture or capture_time to the test case."
138+
)
123139
self.stop()
124140
else:
125141
logger.error("netsniff-ng did not start; skipping capture.")
@@ -128,7 +144,7 @@ def stop(self):
128144
"""
129145
Stops all netsniff-ng processes on the host using pkill.
130146
"""
131-
147+
132148
try:
133149
logger.info("Stopping netsniff-ng using pkill netsniff-ng...")
134150
self.netsniff_process.stop(wait=5)
@@ -155,4 +171,4 @@ def update_filter(self, src_ip=None, dst_ip=None):
155171
self.capture_filter = " and ".join(filters)
156172
elif len(filters) == 1:
157173
self.capture_filter = filters[0]
158-
logger.info(f"Updated capture filter to: {self.capture_filter}")
174+
logger.info(f"Updated capture filter to: {self.capture_filter}")

tests/validation/mtl_engine/RxTxApp.py

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ def prepare_netsniff(
126126
127127
returns: NetsniffRecorder instance or None.
128128
"""
129+
# TODO: remove me as this is now in udp_app.py but it's not needed anymore
129130
if (
130131
capture_cfg
131132
and capture_cfg.get("enable")
@@ -617,14 +618,14 @@ def execute_test(
617618
timeout=timeout,
618619
testcmd=True,
619620
host=remote_host,
620-
background=True
621+
background=True,
621622
)
622-
623+
623624
if netsniff:
624625
netsniff.update_filter(dst_ip=config["tx_sessions"][0]["dip"][0])
625626
netsniff.capture()
626627
logger.info(f"Finished netsniff-ng capture on host {host.name}")
627-
628+
628629
cp.wait(timeout=timeout)
629630
# Capture stdout output for logging
630631
capture_stdout(cp, "RxTxApp")
@@ -746,7 +747,7 @@ def execute_perf_test(
746747
build: str,
747748
test_time: int,
748749
fail_on_error: bool = True,
749-
capture_cfg=None,
750+
netsniff=None,
750751
host=None,
751752
) -> bool:
752753
# Only initialize logging if it hasn't been initialized already
@@ -774,11 +775,6 @@ def execute_perf_test(
774775

775776
logger.info(f"Performance RxTxApp Command: {command}")
776777

777-
# Prepare tcpdump recorder if capture is enabled in the test configuration.
778-
tcpdump = prepare_tcpdump(capture_cfg, host)
779-
netsniff = prepare_netsniff(capture_cfg, host)
780-
background = (tcpdump is not None) or (netsniff is not None)
781-
782778
# For 4TX and 8k streams more timeout is needed
783779
# Also scale timeout with replica count for performance tests
784780
total_replicas = 0
@@ -816,19 +812,15 @@ def execute_perf_test(
816812
timeout=timeout,
817813
testcmd=True,
818814
host=host,
819-
background=background,
815+
background=True,
820816
)
821817

822-
try:
823-
# Start tcpdump capture (blocking, so it captures during traffic)
824-
if tcpdump:
825-
tcpdump.capture(capture_time=capture_cfg.get("capture_time", 0.5))
826-
logger.info("Started performance test tcpdump capture")
827-
if netsniff:
828-
netsniff.start()
829-
logger.info("Started performance test netsniff-ng capture")
830-
finally:
831-
cp.wait()
818+
if netsniff:
819+
netsniff.update_filter(dst_ip=config["tx_sessions"][0]["dip"][0])
820+
netsniff.capture()
821+
logger.info(f"Finished netsniff-ng capture on host {host.name}")
822+
823+
cp.wait()
832824

833825
# Capture stdout output for logging
834826
capture_stdout(cp, "RxTxApp Performance")
@@ -1435,7 +1427,7 @@ def execute_dual_test(
14351427
virtio_user: bool = False,
14361428
rx_timing_parser: bool = False,
14371429
ptp: bool = False,
1438-
capture_cfg=None,
1430+
netsniff=None,
14391431
) -> bool:
14401432
case_id = os.environ["PYTEST_CURRENT_TEST"]
14411433
case_id = case_id[: case_id.rfind("(") - 1]
@@ -1510,11 +1502,10 @@ def execute_dual_test(
15101502
host=tx_host,
15111503
)
15121504

1513-
# Start tcpdump capture if enabled
1514-
tcpdump = prepare_tcpdump(capture_cfg, rx_host)
1515-
if tcpdump:
1516-
tcpdump.capture(capture_time=capture_cfg.get("capture_time", 0.5))
1517-
logger.info("Started dual test tcpdump capture")
1505+
if netsniff:
1506+
netsniff.update_filter(dst_ip=tx_config["tx_sessions"][0]["dip"][0])
1507+
netsniff.capture()
1508+
logger.info(f"Finished netsniff-ng capture on host {netsniff.host.name}")
15181509

15191510
# Wait for both processes
15201511
tx_cp.wait()
@@ -1524,10 +1515,6 @@ def execute_dual_test(
15241515
capture_stdout(tx_cp, "TX RxTxApp")
15251516
capture_stdout(rx_cp, "RX RxTxApp")
15261517

1527-
# Stop tcpdump if it was started
1528-
if tcpdump:
1529-
tcpdump.stop()
1530-
15311518
# Get output from both hosts
15321519
tx_output = tx_cp.stdout_text.splitlines()
15331520
rx_output = rx_cp.stdout_text.splitlines()

tests/validation/mtl_engine/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
LOG_FOLDER = "logs"
55
TESTCMD_LVL = 24 # Custom logging level for test commands
66
FRAMES_CAPTURE = 4 # Number of frames to capture for compliance tests
7-
DOWNLOAD_REPORT_TRIES = 5 # Number of tries to download EBU report
7+
DOWNLOAD_REPORT_TRIES = 5 # Number of tries to download EBU report

tests/validation/mtl_engine/csv_report.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ def csv_add_test(
1212
issue: str,
1313
result_note: str | None = None,
1414
):
15-
report[test_case] = {
15+
res_dict = {
1616
"Test case": test_case,
1717
"Commands": commands,
1818
"Result": result,
1919
"Issue": issue,
2020
"Result note": result_note,
2121
}
22+
if test_case in report:
23+
report[test_case].update(res_dict)
24+
else:
25+
report[test_case] = res_dict
2226

2327

2428
def csv_write_report(filename: str):
@@ -29,6 +33,7 @@ def csv_write_report(filename: str):
2933
"Commands",
3034
"Status",
3135
"Result",
36+
"Compliance",
3237
"Issue",
3338
"Result note",
3439
]
@@ -44,3 +49,10 @@ def csv_write_report(filename: str):
4449
def update_compliance_result(test_case: str, result: str):
4550
if test_case in report:
4651
report[test_case]["Compliance"] = result
52+
else:
53+
report[test_case] = {"Compliance": result}
54+
55+
56+
def get_compliance_result(test_case: str) -> str:
57+
if test_case in report and "Compliance" in report[test_case]:
58+
return report[test_case]["Compliance"]

0 commit comments

Comments
 (0)