[Resubmit] Refactor packet capture to use new logging library.
Use the acts.libs.proc.process library to create a process that logs tcpdump output to local files.
Bug: 120086380
Test: act.py -c <config> -tc WifiNetworkSelectorTest -tb chromeos1-dev-test-station-3
Test: wifi interop tests
Change-Id: I55701e645e7fc903312a795616de16389cf17702
diff --git a/acts/framework/acts/controllers/packet_capture.py b/acts/framework/acts/controllers/packet_capture.py
index 3947bda..caf70b7 100755
--- a/acts/framework/acts/controllers/packet_capture.py
+++ b/acts/framework/acts/controllers/packet_capture.py
@@ -18,8 +18,12 @@
from acts.controllers.ap_lib.hostapd_constants import AP_DEFAULT_CHANNEL_2G
from acts.controllers.ap_lib.hostapd_constants import AP_DEFAULT_CHANNEL_5G
from acts.controllers.utils_lib.ssh import connection
+from acts.controllers.utils_lib.ssh import formatter
from acts.controllers.utils_lib.ssh import settings
+from acts.libs.logging import log_stream
+from acts.libs.proc.process import Process
+import logging
import os
import threading
import time
@@ -44,10 +48,12 @@
def create(configs):
return [PacketCapture(c) for c in configs]
+
def destroy(pcaps):
for pcap in pcaps:
pcap.close()
+
def get_info(pcaps):
return [pcap.ssh_settings.hostname for pcap in pcaps]
@@ -56,17 +62,13 @@
"""Class to maintain packet capture properties after starting tcpdump.
Attributes:
- pid: proccess id of tcpdump
- pcap_dir: tmp dir location where pcap files are saved
+ proc: Process object of tcpdump
pcap_file: pcap file name
- pcap_thread: thread used to push files to logpath
"""
- def __init__(self, pid, pcap_dir, pcap_file, pcap_thread):
+ def __init__(self, proc, pcap_file):
"""Initialize object."""
- self.pid = pid
- self.pcap_dir = pcap_dir
+ self.proc = proc
self.pcap_file = pcap_file
- self.pcap_thread = pcap_thread
class PacketCaptureError(Exception):
@@ -81,8 +83,8 @@
wifi networks; 'wlan2' which is a dual band interface.
Attributes:
- pcap: dict that specifies packet capture properties for a band.
- tmp_dirs: list of tmp directories created for pcap files.
+ pcap_properties: dict that specifies packet capture properties for a
+ band.
"""
def __init__(self, configs):
"""Initialize objects.
@@ -101,7 +103,6 @@
self.pcap_properties = dict()
self._pcap_stop_lock = threading.Lock()
- self.tmp_dirs = []
def _create_interface(self, iface, mode):
"""Create interface of monitor/managed mode.
@@ -157,62 +158,6 @@
network = {}
return scan_networks
- def _check_if_tcpdump_started(self, pcap_log):
- """Check if tcpdump started.
-
- This method ensures that tcpdump has started successfully.
- We look for 'listening on' from the stdout indicating that tcpdump
- is started.
-
- Args:
- pcap_log: log file that has redirected output of starting tcpdump.
-
- Returns:
- True/False if tcpdump is started or not.
- """
- curr_time = time.time()
- timeout = 3
- find_str = 'listening on'
- while time.time() < curr_time + timeout:
- result = self.ssh.run('grep "%s" %s' % (find_str, pcap_log),
- ignore_status=True)
- if result.stdout and find_str in result.stdout:
- return True
- time.sleep(1)
- return False
-
- def _pull_pcap(self, band, pcap_file, log_path):
- """Pulls pcap files to test log path from onhub.
-
- Called by start_packet_capture(). This method moves a pcap file to log
- path once it has reached 50MB.
-
- Args:
- index: param that indicates if the tcpdump is stopped.
- pcap_file: pcap file to move.
- log_path: log path to move the pcap file to.
- """
- curr_no = 0
- while True:
- next_no = curr_no + 1
- curr_fno = '%02i' % curr_no
- next_fno = '%02i' % next_no
- curr_file = '%s%s' % (pcap_file, curr_fno)
- next_file = '%s%s' % (pcap_file, next_fno)
-
- result = self.ssh.run('ls %s' % next_file, ignore_status=True)
- if not result.stderr and next_file in result.stdout:
- self.ssh.pull_file(log_path, curr_file)
- self.ssh.run('rm -rf %s' % curr_file, ignore_status=True)
- curr_no += 1
- continue
-
- with self._pcap_stop_lock:
- if band not in self.pcap_properties:
- self.ssh.pull_file(log_path, curr_file)
- break
- time.sleep(2) # wait before looking for file again
-
def get_wifi_scan_results(self):
"""Starts a wifi scan on wlan2 interface.
@@ -275,66 +220,53 @@
band = 2G starts tcpdump on 'mon0' interface.
band = 5G starts tcpdump on 'mon1' interface.
- This method splits the pcap file every 50MB for 100 files.
- Since, the size of the pcap file could become large, each split file
- is moved to log_path once a new file is generated. This ensures that
- there is no crash on the onhub router due to lack of space.
-
Args:
band: '2g' or '2G' and '5g' or '5G'.
log_path: test log path to save the pcap file.
pcap_file: name of the pcap file.
Returns:
- pid: process id of the tcpdump.
+ pcap_proc: Process object of the tcpdump.
"""
band = band.upper()
if band not in BAND_IFACE.keys() or band in self.pcap_properties:
self.log.error("Invalid band or packet capture already running")
return None
- pcap_dir = self.ssh.run('mktemp -d', ignore_status=True).stdout.rstrip()
- self.tmp_dirs.append(pcap_dir)
- pcap_file = os.path.join(pcap_dir, "%s_%s.pcap" % (pcap_file, band))
- pcap_log = os.path.join(pcap_dir, "%s.log" % pcap_file)
+ pcap_name = "%s_pcap" % band
+ pcap_file = os.path.join(log_path, pcap_name)
- cmd = 'tcpdump -i %s -W 100 -C 50 -w %s > %s 2>&1 & echo $!' % (
- BAND_IFACE[band], pcap_file, pcap_log)
- result = self.ssh.run(cmd, ignore_status=True)
- if not self._check_if_tcpdump_started(pcap_log):
- self.log.error("Failed to start packet capture")
- return None
+ pcap_logger = log_stream.create_logger(
+ pcap_name, base_path=log_path,
+ log_styles=(log_stream.LogStyles.LOG_DEBUG +
+ log_stream.LogStyles.MONOLITH_LOG))
+ pcap_logger.setLevel(logging.DEBUG)
+ cmd = formatter.SshFormatter().format_command(
+ 'tcpdump -i %s -l' %
+ (BAND_IFACE[band]), None, self.ssh_settings)
+ pcap_proc = Process(cmd)
+ pcap_proc.set_on_output_callback(lambda msg: pcap_logger.debug(msg))
+ pcap_proc.start()
- pcap_thread = threading.Thread(target=self._pull_pcap,
- args=(band, pcap_file, log_path))
- pcap_thread.start()
+ self.pcap_properties[band] = PcapProperties(pcap_proc, pcap_file)
+ return pcap_proc
- pid = int(result.stdout)
- self.pcap_properties[band] = PcapProperties(
- pid, pcap_dir, pcap_file, pcap_thread)
- return pid
-
- def stop_packet_capture(self, pid):
+ def stop_packet_capture(self, proc):
"""Stop the packet capture.
Args:
- pid: process id of tcpdump to kill.
+ proc: Process object of tcpdump to kill.
"""
for key, val in self.pcap_properties.items():
- if val.pid == pid:
+ if val.proc is proc:
break
else:
- self.log.error("Failed to stop tcpdump. Invalid PID %s" % pid)
+ self.log.error("Failed to stop tcpdump. Invalid process.")
return
- pcap_dir = val.pcap_dir
- pcap_thread = val.pcap_thread
- self.ssh.run('kill %s' % pid, ignore_status=True)
+ proc.stop()
with self._pcap_stop_lock:
del self.pcap_properties[key]
- pcap_thread.join()
- self.ssh.run('rm -rf %s' % pcap_dir, ignore_status=True)
- self.tmp_dirs.remove(pcap_dir)
def close(self):
"""Cleanup.
@@ -343,6 +275,4 @@
"""
self._cleanup_interface(MON_2G)
self._cleanup_interface(MON_5G)
- for tmp_dir in self.tmp_dirs:
- self.ssh.run('rm -rf %s' % tmp_dir, ignore_status=True)
self.ssh.close()
diff --git a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
index 67bac00..433fdab 100755
--- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
@@ -16,7 +16,7 @@
import logging
import os
-import pprint
+import shutil
import time
from enum import IntEnum
@@ -1710,8 +1710,8 @@
test_name: test name to be used for pcap file name
Returns:
- Dictionary with pid of the tcpdump process as key and log path
- of the file name as the value
+ Dictionary with wifi band as key and the tuple
+ (pcap Process object, log directory) as the value
"""
log_dir = os.path.join(log_path, test_name)
utils.create_dir(log_dir)
@@ -1719,13 +1719,13 @@
bands = [BAND_2G, BAND_5G]
else:
bands = [wifi_band]
- pids = {}
+ procs = {}
for band in bands:
- pid = pcap.start_packet_capture(band, log_dir, test_name)
- pids[pid] = os.path.join(log_dir, test_name)
- return pids
+ proc = pcap.start_packet_capture(band, log_dir, test_name)
+ procs[band] = (proc, os.path.join(log_dir, test_name))
+ return procs
-def stop_pcap(pcap, pids, test_status=None):
+def stop_pcap(pcap, procs, test_status=None):
"""Stop packet capture in monitor mode.
Since, the pcap logs in monitor mode can be very large, we will
@@ -1734,14 +1734,14 @@
Args:
pcap: packet capture object
- pids: dictionary returned by start_pcap
+ procs: dictionary returned by start_pcap
test_status: status of the test case
"""
- for pid, fname in pids.items():
- pcap.stop_packet_capture(pid)
+ for proc, fname in procs.values():
+ pcap.stop_packet_capture(proc)
if test_status:
- os.system('rm -rf %s' % os.path.dirname(fname))
+ shutil.rmtree(os.path.dirname(fname))
def start_cnss_diags(ads):
for ad in ads:
diff --git a/acts/tests/google/wifi/WifiChaosTest.py b/acts/tests/google/wifi/WifiChaosTest.py
index 68dc55a..8a66dd4 100755
--- a/acts/tests/google/wifi/WifiChaosTest.py
+++ b/acts/tests/google/wifi/WifiChaosTest.py
@@ -122,10 +122,10 @@
self.dut.droid.wakeUpNow()
def on_pass(self, test_name, begin_time):
- wutils.stop_pcap(self.pcap, self.pcap_pid, True)
+ wutils.stop_pcap(self.pcap, self.pcap_procs, True)
def on_fail(self, test_name, begin_time):
- wutils.stop_pcap(self.pcap, self.pcap_pid, False)
+ wutils.stop_pcap(self.pcap, self.pcap_procs, False)
self.dut.take_bug_report(test_name, begin_time)
self.dut.cat_adb_log(test_name, begin_time)
@@ -278,7 +278,7 @@
self.get_band_and_chan(ssid)
self.pcap.configure_monitor_mode(self.band, self.chan)
- self.pcap_pid = wutils.start_pcap(
+ self.pcap_procs = wutils.start_pcap(
self.pcap, self.band.lower(), self.log_path, self.test_name)
self.run_connect_disconnect(network, hostname, rpm_port, rpm_ip,
release_ap)
diff --git a/acts/tests/google/wifi/WifiNetworkSelectorTest.py b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
index ffeb6b5..7015636 100644
--- a/acts/tests/google/wifi/WifiNetworkSelectorTest.py
+++ b/acts/tests/google/wifi/WifiNetworkSelectorTest.py
@@ -74,7 +74,7 @@
self.dut.ed.clear_all_events()
if hasattr(self, 'packet_capture'):
- self.pcap_pids = wutils.start_pcap(
+ self.pcap_procs = wutils.start_pcap(
self.packet_capture, 'dual', self.log_path, self.test_name)
def teardown_test(self):
@@ -84,11 +84,11 @@
def on_pass(self, test_name, begin_time):
if hasattr(self, 'packet_capture'):
- wutils.stop_pcap(self.packet_capture, self.pcap_pids, True)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, True)
def on_fail(self, test_name, begin_time):
if hasattr(self, 'packet_capture'):
- wutils.stop_pcap(self.packet_capture, self.pcap_pids, False)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
self.dut.take_bug_report(test_name, begin_time)
self.dut.cat_adb_log(test_name, begin_time)