-
Notifications
You must be signed in to change notification settings - Fork 74
Test: Automation for netsniff-ng packet capture #1241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MateuszGrabuszynski
wants to merge
41
commits into
main
Choose a base branch
from
netsniff-autom
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
fb419de
Add dual host ffmpeg tests for various video formats and configurations
Falron98 ef780d0
Fix dual basic tests
Falron98 1e1cab7
Fix dual ffmpeg tests;
Falron98 9bac7a5
Fix linter issues
Falron98 442b439
Add: netsniff-ng capturing capabilities based on already-created tcpd…
MateuszGrabuszynski 57dffb1
Doc: Modifying comments about packet capture
MateuszGrabuszynski bda5c5a
Add: netsniff-ng can be executed in all tests where tcpdump capture w…
MateuszGrabuszynski 7a31926
Lint: applying isort recommendations
MateuszGrabuszynski d6fcab1
Fix: improper order in if-else oneliner
MateuszGrabuszynski 0258dfe
Merge branch 'ffmpeg-dual' into netsniff-autom
MateuszGrabuszynski 565f4cb
Test: Fixing the netsniff-ng preparation
MateuszGrabuszynski ccb3361
Test: Add netsniff-ng to dual-node tests
MateuszGrabuszynski 4f6754f
Removing unnecessary ands from comments; Removing unnecessarily added…
MateuszGrabuszynski f4631c0
filter -> self.filter so no -f None is present in netsniff-ng's command
MateuszGrabuszynski e964bd3
Adding missing quotation marks around filter, removing unnecessary de…
MateuszGrabuszynski 46ddf17
Add filtering to netsniff-ng, so only the test streams are captured, …
MateuszGrabuszynski 1d2b2f3
Adding datetime into generated filename
MateuszGrabuszynski 76545e2
Fixing lack of space in netsniff-ng filter
MateuszGrabuszynski 01bad5b
filter -> capture_filter to avoid shadowing over Python's filter func…
MateuszGrabuszynski a77c7bd
Documentation for netsniff installation
MateuszGrabuszynski 48bec15
Adding usage docs
MateuszGrabuszynski 2b05644
Merge branch 'main' into netsniff-autom
MateuszGrabuszynski 914a2db
Changing ramdisk fixture so it is universal
MateuszGrabuszynski 59f464f
Changing netsniff.capture() to netsniff.start() so the capture is non…
MateuszGrabuszynski ebacb84
Compliance uploads and checks added (fixes required)
MateuszGrabuszynski 37c13fc
Merge branch 'main' into netsniff-autom
MateuszGrabuszynski efcadb7
Re-adding netsniff packet capture after wrongly removing it during me…
MateuszGrabuszynski ae6c12f
Adding pcap_capture fixture stub
MateuszGrabuszynski da56832
Moving the capturing process into a fixture
MateuszGrabuszynski c69405a
Working EBU LIST compliance check upload
MateuszGrabuszynski 8ed9547
Fixing linter issues
MateuszGrabuszynski 48d5ea9
Further linter-raised fixes
MateuszGrabuszynski 9b08329
Further linter changes in pcap_compliance.py
MateuszGrabuszynski 60ec24e
Further linter changes in pcap_compliance.py
MateuszGrabuszynski a78a592
Merge branch 'main' into netsniff-autom
MateuszGrabuszynski f7f8abd
Removing netsniff and tcpdump references in GstreamerApp.py, as they …
MateuszGrabuszynski eeeee84
Removing unused import in GstreamerApp
MateuszGrabuszynski 5e17b76
Merge remote-tracking branch 'origin/main' into netsniff-autom
andremiszcz 45b7a4b
Refactor and prepare working scenario with full compliance check
andremiszcz 7643980
Adjust ST20 and ST30 tests with netsniff
andremiszcz 3d8c045
Merge remote-tracking branch 'origin/main' into netsniff-autom
andremiszcz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import os | ||
import sys | ||
|
||
from csv_report import TestCSVReport | ||
from pcap_compliance import PcapComplianceClient | ||
|
||
# Usage: | ||
# python3 check_compliance.py <uuid> <test_name> [csv_path] | ||
# <uuid> : UUID of the uploaded PCAP file (from upload step) | ||
# <test_name> : Name for the test (used for report directory) | ||
# [csv_path] : (Optional) Path to CSV file for appending compliance results and path to logs | ||
|
||
if len(sys.argv) not in (3, 4): | ||
print("Usage: python3 check_compliance.py <uuid> <test_name> [csv_path]") | ||
sys.exit(1) | ||
|
||
uuid = sys.argv[1] | ||
test_name = sys.argv[2] | ||
csv_path = sys.argv[3] if len(sys.argv) == 4 else None | ||
|
||
# Step 1: Run compliance check and generate report/logs using PcapComplianceClient | ||
checker = PcapComplianceClient() | ||
checker.authenticate() | ||
checker.pcap_id = uuid | ||
report_path = checker.download_report(test_name) | ||
is_compliant = checker.check_compliance(report_path) | ||
print(f"Report and logs saved in: {os.path.abspath(checker.report_dir)}") | ||
|
||
# Step 2 (optional): If csv_path is provided, append compliance result to CSV | ||
if csv_path: | ||
report = TestCSVReport(csv_path) | ||
compliance_result = "PASSED" if is_compliant else "FAILED" | ||
report.add_result(test_name, compliance_result, checker.report_dir) | ||
print(f"Compliance result added to CSV: {csv_path}") | ||
|
||
# Step 3 Delete the PCAP analysis from the EBU server | ||
checker.delete_pcap(uuid) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import csv | ||
import os | ||
|
||
|
||
class TestCSVReport: | ||
def __init__(self, csv_path): | ||
""" | ||
Initialize the CSV report. | ||
If the file does not exist, create it and write the header row. | ||
""" | ||
self.csv_path = csv_path | ||
# Write header if file does not exist | ||
if not os.path.isfile(self.csv_path): | ||
with open(self.csv_path, mode="w", newline="") as csvfile: | ||
writer = csv.writer(csvfile) | ||
writer.writerow( | ||
[ | ||
"test_name", # Name of the test | ||
"compliance_result", # Compliance check result (e.g., PASSED/FAILED) | ||
"logs_path", # Final result (e.g., PASSED/FAILED) | ||
] | ||
) | ||
|
||
def add_result(self, test_name, compliance_result, logs_path): | ||
""" | ||
Append a new row with the test results to the CSV file. | ||
""" | ||
with open(self.csv_path, mode="a", newline="") as csvfile: | ||
writer = csv.writer(csvfile) | ||
writer.writerow([test_name, compliance_result, logs_path]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import yaml | ||
|
||
|
||
def load_ebu_config(config_path: str): | ||
with open(config_path, "r") as f: | ||
config = yaml.safe_load(f) | ||
instance = config["instances"] | ||
config_response = { | ||
"ebu_ip": instance.get("name", ""), | ||
"username": instance.get("username", ""), | ||
"password": instance.get("password", ""), | ||
} | ||
return config_response |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import datetime | ||
import json | ||
import os | ||
|
||
import requests | ||
import yaml | ||
|
||
|
||
class PcapComplianceClient: | ||
def __init__( | ||
self, | ||
pcap_file=None, | ||
config_path="ebu_list.yaml", | ||
proxies={"http": "", "https": "", "ftp": ""}, | ||
): | ||
""" | ||
Initialize the client with optional PCAP file and config path. | ||
Loads EBU server IP and credentials from the YAML config. | ||
""" | ||
self.pcap_file = pcap_file | ||
self.token = None | ||
self.ebu_ip = None | ||
self.user = None | ||
self.password = None | ||
self.pcap_id = None | ||
self.report_dir = None | ||
self.proxies = proxies | ||
self.session = requests.Session() | ||
self.session.trust_env = False # Do not use system proxy settings | ||
|
||
# Load EBU IP and credentials from YAML config | ||
if config_path: | ||
with open(config_path, "r") as f: | ||
config = yaml.safe_load(f) | ||
instance = config["instances"] | ||
self.ebu_ip = instance.get("name", "") | ||
self.user = instance.get("username", "") | ||
self.password = instance.get("password", "") | ||
|
||
def authenticate(self): | ||
""" | ||
Authenticate with the EBU server and store the access token. | ||
""" | ||
url = f"http://{self.ebu_ip}/auth/login" | ||
headers = {"Content-Type": "application/json"} | ||
data = {"username": self.user, "password": self.password} | ||
response = self.session.post( | ||
url, headers=headers, json=data, verify=False, proxies=self.proxies | ||
) | ||
response.raise_for_status() | ||
self.token = response.json().get("content", {}).get("token") | ||
if not self.token: | ||
raise Exception("Authentication failed: No token received.") | ||
|
||
def upload_pcap(self): | ||
""" | ||
Upload the PCAP file to the EBU server and store the returned UUID. | ||
Returns the UUID of the uploaded PCAP. | ||
""" | ||
url = f"http://{self.ebu_ip}/api/pcap" | ||
headers = {"Authorization": f"Bearer {self.token}"} | ||
if self.pcap_file: | ||
with open(self.pcap_file, "rb") as f: | ||
files = { | ||
"pcap": ( | ||
os.path.basename(self.pcap_file), | ||
f, | ||
"application/vnd.tcpdump.pcap", | ||
) | ||
} | ||
response = self.session.put( | ||
url, | ||
headers=headers, | ||
files=files, | ||
verify=False, | ||
proxies=self.proxies, | ||
) | ||
response.raise_for_status() | ||
self.pcap_id = response.json().get("uuid") | ||
if not self.pcap_id: | ||
raise Exception("Upload failed: No UUID received.") | ||
print(f"Extracted UUID: >>>{self.pcap_id}<<<") | ||
return self.pcap_id | ||
|
||
def download_report(self, test_name=None): | ||
""" | ||
Download the compliance report for the uploaded PCAP file. | ||
Saves the report as a JSON file in a timestamped directory. | ||
Returns the path to the saved report. | ||
""" | ||
url = f"http://{self.ebu_ip}/api/pcap/{self.pcap_id}/report?type=json" | ||
headers = {"Authorization": f"Bearer {self.token}"} | ||
response = self.session.get( | ||
url, headers=headers, verify=False, proxies=self.proxies | ||
) | ||
response.raise_for_status() | ||
if test_name is not None: | ||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") | ||
self.report_dir = os.path.join("reports", test_name, timestamp) | ||
os.makedirs(self.report_dir, exist_ok=True) | ||
report_path = os.path.join(self.report_dir, f"{self.pcap_id}.json") | ||
with open(report_path, "w") as f: | ||
json.dump(response.json(), f, indent=2) | ||
return report_path | ||
return response.json() | ||
|
||
def check_compliance(self, report_path): | ||
""" | ||
Check the compliance result from the downloaded report. | ||
Prints the result, writes PASSED/FAILED files, and lists error IDs if failed. | ||
Returns True if compliant, False otherwise. | ||
""" | ||
with open(report_path, "r") as f: | ||
report = json.load(f) | ||
is_compliant = report.get("not_compliant_streams", 1) == 0 | ||
result_file = "PASSED" if is_compliant else "FAILED" | ||
print(f"Result: {result_file}") | ||
with open(os.path.join(self.report_dir, result_file), "w") as f_result: | ||
if is_compliant: | ||
f_result.write("") | ||
else: | ||
error_ids = [] | ||
for err in report.get("summary", {}).get("error_list", []): | ||
error_id = err.get("value", {}).get("id") | ||
print(error_id) | ||
error_ids.append(str(error_id)) | ||
for error_id in error_ids: | ||
f_result.write(f"{error_id}\n") | ||
print(f"Json file saved: file://{os.path.abspath(report_path)}") | ||
return is_compliant | ||
|
||
def delete_pcap(self, pcap_id=None): | ||
""" | ||
Delete the PCAP file and its report from the EBU server. | ||
If pcap_id is not provided, uses self.pcap_id. | ||
""" | ||
if pcap_id is None: | ||
pcap_id = self.pcap_id | ||
if not pcap_id: | ||
raise ValueError("No PCAP ID provided for deletion.") | ||
url = f"http://{self.ebu_ip}/api/pcap/{pcap_id}" | ||
headers = {"Authorization": f"Bearer {self.token}"} | ||
response = self.session.delete( | ||
url, headers=headers, verify=False, proxies=self.proxies | ||
) | ||
if response.status_code == 200: | ||
print(f"PCAP {pcap_id} deleted successfully from EBU server.") | ||
else: | ||
print( | ||
f"Failed to delete PCAP {pcap_id}: {response.status_code} {response.text}" | ||
) | ||
response.raise_for_status() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import argparse | ||
import os | ||
|
||
if __name__ == "__main__": | ||
from pcap_compliance import PcapComplianceClient | ||
else: | ||
from compliance.pcap_compliance import PcapComplianceClient | ||
|
||
|
||
def parse_args(): | ||
parser = argparse.ArgumentParser( | ||
description="Upload a PCAP file to the EBU server." | ||
) | ||
parser.add_argument( | ||
"--pcap_file_path", | ||
type=str, | ||
required=True, | ||
help="Full path to the PCAP file to upload.", | ||
) | ||
parser.add_argument( | ||
"--ip", type=str, required=True, help="IP address to the EBU LIST service." | ||
) | ||
parser.add_argument( | ||
"--login", type=str, required=True, help="Login to the EBU LIST service." | ||
) | ||
parser.add_argument( | ||
"--password", type=str, required=True, help="Password to the EBU LIST service." | ||
) | ||
parser.add_argument( | ||
"--proxy", | ||
type=str, | ||
required=False, | ||
help="Proxy used to upload to the EBU LIST service.", | ||
) | ||
return parser.parse_args() | ||
|
||
|
||
def upload_pcap(file_path, ip, login, password, proxies): | ||
# Check for login and password | ||
if not ip or not login or not password: | ||
raise Exception("IP address, login and password are required.") | ||
|
||
# Check if the file exists before proceeding | ||
if not os.path.isfile(file_path): | ||
raise Exception(f"File not found: {file_path}") | ||
|
||
# Create the uploader object and upload the PCAP file | ||
# Empty config_path to avoid loading from non-existing YAML file | ||
uploader = PcapComplianceClient( | ||
pcap_file=file_path, config_path="", proxies=proxies | ||
) | ||
uploader.ebu_ip = ip | ||
uploader.user = login | ||
uploader.password = password | ||
uploader.authenticate() # Authenticate with the EBU server | ||
uuid = uploader.upload_pcap() # Upload the PCAP file and get the UUID | ||
|
||
return uuid | ||
|
||
|
||
if __name__ == "__main__": | ||
args = parse_args() | ||
# TODO: Handle proxies better, so each type can have a proper value | ||
proxies = None | ||
if args.proxy is not None: | ||
proxies = {"http": args.proxy, "https": args.proxy, "ftp": args.proxy} | ||
uuid = upload_pcap(args.pcap_file_path, args.ip, args.login, args.password, proxies) | ||
# Print extractable UUID | ||
print(f">>>UUID>>>{uuid}<<<UUID<<<") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
metadata: | ||
version: '1.0' | ||
instances: | ||
name: {{ ebu_ip }} | ||
username: {{ ebu_login }} | ||
password: {{ ebu_password }} | ||
verify: true | ||
token: json_token |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.