refactor: ruff autofixes

This commit is contained in:
Mark Paronyan committed 2026-09-27 15:55:10 +03:00
1 parent 2ac637c662
commit a0450b6bce
455 files changed
+12322 -14762

No files matched your search

+13 -21
View File
@@ -83,7 +83,7 @@ class DockerHTTPClient:
self._connected = True
# Parse API version
api_version = parse_version(docker_info['ApiVersion'])
api_version = parse_version(docker_info["ApiVersion"])
docker_version = docker_info["Version"]
logger.info(f"Connected to Docker {docker_version}, API {api_version}")
@@ -102,7 +102,7 @@ class DockerHTTPClient:
logger.info(f"Using Docker API version {self._api_version}")
else:
# Use Docker daemon's actual API version
self._api_version = docker_info['ApiVersion']
self._api_version = docker_info["ApiVersion"]
logger.info(f"Using Docker API version {self._api_version} (daemon native)")
except (aiohttp.ClientError, FileNotFoundError) as e:
@@ -124,7 +124,7 @@ class DockerHTTPClient:
Response JSON data
"""
# Check connection and version on first request
check_connection = kwargs.pop('check_connection', True)
check_connection = kwargs.pop("check_connection", True)
if check_connection and not self._connected:
await self._check_connection()
@@ -147,22 +147,21 @@ class DockerHTTPClient:
except RuntimeError as e:
# Retry with lower API version if Docker daemon doesn't support current version
error_msg = str(e)
if ("400" in error_msg or "not found" in error_msg.lower()) and self._api_version == DOCKER_PREFERRED_API_VERSION:
logger.warning(f"Docker daemon doesn't support API version {self._api_version}, falling back to {DOCKER_MINIMUM_API_VERSION}")
if (
"400" in error_msg or "not found" in error_msg.lower()
) and self._api_version == DOCKER_PREFERRED_API_VERSION:
logger.warning(
f"Docker daemon doesn't support API version {self._api_version}, falling back to {DOCKER_MINIMUM_API_VERSION}"
)
self._api_version = DOCKER_MINIMUM_API_VERSION
return await self._request(method, endpoint, check_connection=False, **kwargs)
raise
async def create_network(self, name: str, driver: str = "bridge", subnet: str = None):
"""Create Docker network."""
data = {
"Name": name,
"Driver": driver
}
data = {"Name": name, "Driver": driver}
if subnet:
data["IPAM"] = {
"Config": [{"Subnet": subnet}]
}
data["IPAM"] = {"Config": [{"Subnet": subnet}]}
await self._request("POST", "networks/create", json=data)
async def get_network(self, name: str):
@@ -176,19 +175,12 @@ class DockerHTTPClient:
async def create_container(self, name: str, image: str, **kwargs):
"""Create container."""
data = {
"Image": image,
"name": name,
"HostConfig": {},
"NetworkingConfig": {}
}
data = {"Image": image, "name": name, "HostConfig": {}, "NetworkingConfig": {}}
# Handle network config
if "network" in kwargs:
network_name = kwargs.pop("network")
data["NetworkingConfig"]["EndpointsConfig"] = {
network_name: {}
}
data["NetworkingConfig"]["EndpointsConfig"] = {network_name: {}}
# Handle environment variables
if "environment" in kwargs:
@@ -92,10 +92,7 @@ logger = logging.getLogger(__name__)
def setup_logging(verbose: bool = False):
"""Setup logging configuration."""
level = logging.DEBUG if verbose else logging.INFO
logging.basicConfig(
level=level,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logging.basicConfig(level=level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
async def cmd_start(args) -> int:
@@ -119,7 +116,7 @@ async def cmd_start(args) -> int:
image=args.image,
memory=args.memory,
cpus=args.cpus,
pids_limit=args.pids_limit
pids_limit=args.pids_limit,
)
print(json.dumps(result, indent=2))
@@ -143,10 +140,7 @@ async def cmd_stop(args) -> int:
"""
manager = WebWiresharkManager()
try:
await manager.stop_wireshark_session(
project_id=args.project_id,
link_id=args.link_id
)
await manager.stop_wireshark_session(project_id=args.project_id, link_id=args.link_id)
print(json.dumps({"status": "stopped"}))
return 0
except Exception as e:
@@ -172,7 +166,7 @@ async def cmd_restart(args) -> int:
project_id=args.project_id,
link_id=args.link_id,
jwt_token=args.jwt_token,
capture_stream_url=args.capture_url
capture_stream_url=args.capture_url,
)
print(json.dumps(result, indent=2))
return 0
@@ -274,14 +268,8 @@ async def cmd_delete(args) -> int:
def create_parser() -> argparse.ArgumentParser:
"""Create command line argument parser."""
parser = argparse.ArgumentParser(
description="Web Wireshark container management"
)
parser.add_argument(
"--verbose", "-v",
action="store_true",
help="Enable verbose logging"
)
parser = argparse.ArgumentParser(description="Web Wireshark container management")
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose logging")
subparsers = parser.add_subparsers(dest="command", help="Commands")
@@ -290,32 +278,13 @@ def create_parser() -> argparse.ArgumentParser:
start_parser.add_argument("--project-id", required=True, type=validate_uuid, help="Project ID")
start_parser.add_argument("--link-id", required=True, type=validate_uuid, help="Link ID")
start_parser.add_argument("--jwt-token", required=True, help="JWT token")
start_parser.add_argument("--capture-url", help="Capture stream URL (auto-detected if not provided)")
start_parser.add_argument(
"--capture-url",
help="Capture stream URL (auto-detected if not provided)"
)
start_parser.add_argument(
"--image",
default="gns3/web-wireshark:latest",
help="Docker image (default: gns3/web-wireshark:latest)"
)
start_parser.add_argument(
"--memory",
default="2g",
help="Memory limit (default: 2g)"
)
start_parser.add_argument(
"--cpus",
type=float,
default=1.0,
help="CPU cores (default: 1.0)"
)
start_parser.add_argument(
"--pids-limit",
type=int,
default=1000,
help="Process limit (default: 1000)"
"--image", default="gns3/web-wireshark:latest", help="Docker image (default: gns3/web-wireshark:latest)"
)
start_parser.add_argument("--memory", default="2g", help="Memory limit (default: 2g)")
start_parser.add_argument("--cpus", type=float, default=1.0, help="CPU cores (default: 1.0)")
start_parser.add_argument("--pids-limit", type=int, default=1000, help="Process limit (default: 1000)")
# Stop command
stop_parser = subparsers.add_parser("stop", help="Stop Web Wireshark session")
@@ -327,10 +296,7 @@ def create_parser() -> argparse.ArgumentParser:
restart_parser.add_argument("--project-id", required=True, type=validate_uuid, help="Project ID")
restart_parser.add_argument("--link-id", required=True, type=validate_uuid, help="Link ID")
restart_parser.add_argument("--jwt-token", required=True, help="JWT token")
restart_parser.add_argument(
"--capture-url",
help="Capture stream URL (auto-detected if not provided)"
)
restart_parser.add_argument("--capture-url", help="Capture stream URL (auto-detected if not provided)")
# Stop all command
stop_all_parser = subparsers.add_parser("stop-all", help="Stop all sessions")
+101 -137
View File
@@ -63,15 +63,16 @@ class WebWiresharkManager:
"""
try:
proc = await asyncio.create_subprocess_exec(
"docker", "exec", container_id,
"bash", "-c", "echo 'ping'",
"docker",
"exec",
container_id,
"bash",
"-c",
"echo 'ping'",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, _ = await asyncio.wait_for(
proc.communicate(),
timeout=self.CONTAINER_EXEC_TIMEOUT
stderr=asyncio.subprocess.PIPE,
)
stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=self.CONTAINER_EXEC_TIMEOUT)
return proc.returncode == 0 and b"ping" in stdout
except asyncio.TimeoutError:
logger.warning(f"Container {container_id[:12]} health check timeout")
@@ -99,16 +100,17 @@ class WebWiresharkManager:
try:
proc = await asyncio.create_subprocess_exec(
"docker", "exec", container_id,
"bash", "-c", command,
"docker",
"exec",
container_id,
"bash",
"-c",
command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
stderr=asyncio.subprocess.PIPE,
)
try:
stdout, stderr = await asyncio.wait_for(
proc.communicate(),
timeout=timeout
)
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout)
return (proc.returncode, stdout.decode().strip(), stderr.decode().strip())
except asyncio.TimeoutError:
logger.error(f"Command timeout after {timeout}s: {command}")
@@ -142,7 +144,7 @@ class WebWiresharkManager:
logger.debug(f"No processes found matching pattern '{pattern}'")
return
pids = pids_str.replace('\n', ' ')
pids = pids_str.replace("\n", " ")
logger.info(f"Found matching processes for '{pattern}': PIDs={pids}")
# Kill the processes using their container-local PIDs
@@ -170,10 +172,13 @@ class WebWiresharkManager:
try:
# Get container init PID from host perspective
proc = await asyncio.create_subprocess_exec(
"docker", "inspect", container_id,
"--format", "{{.State.Pid}}",
"docker",
"inspect",
container_id,
"--format",
"{{.State.Pid}}",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
stderr=asyncio.subprocess.PIPE,
)
stdout, _ = await proc.communicate()
container_init_pid = stdout.decode().strip()
@@ -187,15 +192,13 @@ class WebWiresharkManager:
# Check for residual processes from host perspective
proc = await asyncio.create_subprocess_exec(
"ps", "-eo", "pid,ppid,args",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
"ps", "-eo", "pid,ppid,args", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
stdout, _ = await proc.communicate()
# Build parent->children mapping
children_map = {}
for line in stdout.decode().strip().split('\n'):
for line in stdout.decode().strip().split("\n"):
parts = line.split(None, 2)
if len(parts) < 3:
continue
@@ -218,14 +221,15 @@ class WebWiresharkManager:
# Check if any descendant processes match display patterns
import re
patterns = [
f'xpra.*:{display}',
f'Xvfb.*:{display}',
f'wireshark.*:{display}',
f'pulseaudio.*display=:{display}'
f"xpra.*:{display}",
f"Xvfb.*:{display}",
f"wireshark.*:{display}",
f"pulseaudio.*display=:{display}",
]
for line in stdout.decode().strip().split('\n'):
for line in stdout.decode().strip().split("\n"):
parts = line.split(None, 2)
if len(parts) < 3:
continue
@@ -248,13 +252,11 @@ class WebWiresharkManager:
# /proc/<pid>/root points to container filesystem
lock_paths = [
f"/proc/{container_init_pid}/root/tmp/.X{display}-lock",
f"/proc/{container_init_pid}/root/tmp/.X11-unix/X{display}"
f"/proc/{container_init_pid}/root/tmp/.X11-unix/X{display}",
]
for lock_path in lock_paths:
proc = await asyncio.create_subprocess_exec(
"test", "-e", lock_path,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
"test", "-e", lock_path, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
await proc.communicate()
if proc.returncode == 0:
@@ -264,9 +266,7 @@ class WebWiresharkManager:
# Check for xpra socket files
socket_path = f"/proc/{container_init_pid}/root/run/user/1000/xpra/{display}/socket"
proc = await asyncio.create_subprocess_exec(
"test", "-e", socket_path,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
"test", "-e", socket_path, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
await proc.communicate()
if proc.returncode == 0:
@@ -298,10 +298,13 @@ class WebWiresharkManager:
try:
# Get container init PID from host perspective
proc = await asyncio.create_subprocess_exec(
"docker", "inspect", container_id,
"--format", "{{.State.Pid}}",
"docker",
"inspect",
container_id,
"--format",
"{{.State.Pid}}",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
stderr=asyncio.subprocess.PIPE,
)
stdout, _ = await proc.communicate()
container_init_pid = stdout.decode().strip()
@@ -314,9 +317,7 @@ class WebWiresharkManager:
# List all processes from host perspective with PID, PPID, and command
# This is faster than docker exec and allows us to walk the process tree
proc = await asyncio.create_subprocess_exec(
"ps", "-eo", "pid,ppid,args",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
"ps", "-eo", "pid,ppid,args", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
stdout, _ = await proc.communicate()
@@ -328,7 +329,7 @@ class WebWiresharkManager:
children_map = {} # ppid -> [pid]
process_info = {} # pid -> (ppid, command)
for line in stdout.decode().strip().split('\n'):
for line in stdout.decode().strip().split("\n"):
parts = line.split(None, 2)
if len(parts) < 3:
continue
@@ -371,9 +372,7 @@ class WebWiresharkManager:
logger.info(f"Killing {len(matching_pids)} processes: {matching_pids}")
# Kill all matching PIDs from host perspective
proc = await asyncio.create_subprocess_exec(
"kill", "-9", *matching_pids,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
"kill", "-9", *matching_pids, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
await proc.communicate()
logger.debug("Kill completed from host perspective")
@@ -396,16 +395,12 @@ class WebWiresharkManager:
"""
try:
# Combine all patterns into a single regex using OR operator
combined_pattern = '|'.join(f'({pattern})' for pattern in patterns)
combined_pattern = "|".join(f"({pattern})" for pattern in patterns)
# Single pgrep to find all matching processes
pgrep_cmd = f'pids=$(pgrep -f "{combined_pattern}" 2>/dev/null || true); if [ -n "$pids" ]; then echo "Found processes: $pids"; kill -9 $pids 2>/dev/null || true; fi'
returncode, stdout, stderr = await self._exec_in_container(
container_id,
pgrep_cmd,
timeout=5
)
returncode, stdout, stderr = await self._exec_in_container(container_id, pgrep_cmd, timeout=5)
if "Found processes:" in stdout:
logger.info(stdout.strip())
@@ -422,10 +417,12 @@ class WebWiresharkManager:
"""
# Clean up X lock files and xpra socket files in a single docker exec
# This reduces docker exec calls from 2 to 1, improving stop performance
cmd = (f"rm -f /tmp/.X{display}-lock /tmp/.X11-unix/X{display} "
f"/run/user/1000/xpra/{display}/socket "
f"/run/user/1000/xpra/*-{display} "
f"/home/gns3/.xpra/*-{display} 2>/dev/null || true")
cmd = (
f"rm -f /tmp/.X{display}-lock /tmp/.X11-unix/X{display} "
f"/run/user/1000/xpra/{display}/socket "
f"/run/user/1000/xpra/*-{display} "
f"/home/gns3/.xpra/*-{display} 2>/dev/null || true"
)
returncode, stdout, stderr = await self._exec_in_container(container_id, cmd)
logger.debug(f"Cleanup X locks and xpra sockets: returncode={returncode}")
@@ -436,9 +433,11 @@ class WebWiresharkManager:
container_id: Container ID
display: Display number (e.g., 10210)
"""
cmd = (f"rm -f /run/user/1000/xpra/{display}/socket "
f"/run/user/1000/xpra/*-{display} "
f"/home/gns3/.xpra/*-{display} 2>/dev/null || true")
cmd = (
f"rm -f /run/user/1000/xpra/{display}/socket "
f"/run/user/1000/xpra/*-{display} "
f"/home/gns3/.xpra/*-{display} 2>/dev/null || true"
)
returncode, stdout, stderr = await self._exec_in_container(container_id, cmd)
logger.debug(f"Cleanup socket files: returncode={returncode}")
@@ -457,20 +456,15 @@ class WebWiresharkManager:
return 0
# Extract number and unit
match = re.match(r'(\d+(?:\.\d+)?)\s*([kmg]b?)?', memory_str)
match = re.match(r"(\d+(?:\.\d+)?)\s*([kmg]b?)?", memory_str)
if not match:
raise ValueError(f"Invalid memory format: {memory_str}")
value = float(match.group(1))
unit = match.group(2) or 'b'
unit = match.group(2) or "b"
# Convert to bytes
multipliers = {
'b': 1,
'k': 1024,
'm': 1024 * 1024,
'g': 1024 * 1024 * 1024
}
multipliers = {"b": 1, "k": 1024, "m": 1024 * 1024, "g": 1024 * 1024 * 1024}
unit = unit[0] # Take first character
return int(value * multipliers.get(unit, 1))
@@ -511,8 +505,7 @@ class WebWiresharkManager:
if container_id:
try:
returncode, stdout, stderr = await self._exec_in_container(
container_id,
"cat /proc/net/route | grep -E '^eth0\\s+00000000' | awk '{print $3}' | head -1"
container_id, "cat /proc/net/route | grep -E '^eth0\\s+00000000' | awk '{print $3}' | head -1"
)
logger.info(f"Gateway detection - fallback method: returncode={returncode}, stdout='{stdout}'")
if returncode == 0 and stdout.strip():
@@ -557,8 +550,7 @@ class WebWiresharkManager:
# Use 'hostname -I' to get all IP addresses and take the first one
# This works on most Linux systems and is more portable than 'ip' command
returncode, stdout, stderr = await self._exec_in_container(
container_id,
"hostname -I 2>/dev/null | awk '{print $1}'"
container_id, "hostname -I 2>/dev/null | awk '{print $1}'"
)
if returncode == 0 and stdout.strip():
container_ip = stdout.strip()
@@ -585,7 +577,7 @@ class WebWiresharkManager:
"""
try:
parsed = urlparse(url)
if parsed.hostname in ('localhost', '127.0.0.1', '0.0.0.0'):
if parsed.hostname in ("localhost", "127.0.0.1", "0.0.0.0"):
gateway = await self._get_container_gateway_ip(container_id)
if gateway:
fixed_url = f"{parsed.scheme}://{gateway}:{parsed.port or 3080}{parsed.path}"
@@ -627,11 +619,7 @@ class WebWiresharkManager:
config = Config.instance()
subnet = getattr(config.settings.WebWireshark, "network_subnet", "172.31.0.0/22")
logger.info(f"Creating network {self.network_name} with subnet {subnet}")
await self.docker.create_network(
self.network_name,
driver="bridge",
subnet=subnet
)
await self.docker.create_network(self.network_name, driver="bridge", subnet=subnet)
async def get_or_create_container(
self,
@@ -640,7 +628,7 @@ class WebWiresharkManager:
memory: str = "2g",
memory_swap: str = None,
cpus: float = 1.0,
pids_limit: int = 1000
pids_limit: int = 1000,
) -> str:
"""Get or create project's Web Wireshark container.
@@ -716,21 +704,15 @@ class WebWiresharkManager:
"PidsLimit": pids_limit,
"RestartPolicy": {"Name": "unless-stopped"},
"Init": True, # Use init system (tini) as PID 1 to reap zombie processes
"LogConfig": {
"Type": "json-file",
"Config": {
"max-size": "10m",
"max-file": "3"
}
}
"LogConfig": {"Type": "json-file", "Config": {"max-size": "10m", "max-file": "3"}},
}
# Health check configuration
health_config = {
"Test": ["CMD-SHELL", "xpra list"],
"Interval": 30000000000, # 30 seconds (nanoseconds)
"Timeout": 10000000000, # 10 seconds
"Retries": 3
"Timeout": 10000000000, # 10 seconds
"Retries": 3,
}
try:
@@ -740,11 +722,7 @@ class WebWiresharkManager:
network=self.network_name,
host_config=host_config,
health_config=health_config,
environment={
"XDG_RUNTIME_DIR": "/run/user/1000",
"LANG": "C.UTF-8",
"LC_ALL": "C.UTF-8"
}
environment={"XDG_RUNTIME_DIR": "/run/user/1000", "LANG": "C.UTF-8", "LC_ALL": "C.UTF-8"},
)
except RuntimeError as e:
error_msg = str(e)
@@ -773,7 +751,7 @@ class WebWiresharkManager:
memory: str = "2g",
memory_swap: Optional[str] = None,
cpus: float = 1.0,
pids_limit: int = 1000
pids_limit: int = 1000,
) -> dict:
"""Start Web Wireshark session.
@@ -799,20 +777,10 @@ class WebWiresharkManager:
# Auto-detect capture_stream_url if not provided
if not capture_stream_url:
gns3_url = self.detect_gns3_url()
capture_stream_url = (
f"{gns3_url}/v3/projects/{project_id}/links/"
f"{link_id}/capture/stream"
)
capture_stream_url = f"{gns3_url}/v3/projects/{project_id}/links/{link_id}/capture/stream"
logger.info(f"Auto-detected capture stream URL: {capture_stream_url}")
container_id = await self.get_or_create_container(
project_id,
image,
memory,
memory_swap,
cpus,
pids_limit
)
container_id = await self.get_or_create_container(project_id, image, memory, memory_swap, cpus, pids_limit)
container_name = f"gns3-wireshark-{project_id}"
# Fix localhost URL after we have container_id
@@ -828,13 +796,15 @@ class WebWiresharkManager:
has_process_residuals, has_socket_residuals = await self._check_residuals_exist(container_id, display)
if has_process_residuals or has_socket_residuals:
logger.info(f"Found residual processes={has_process_residuals} sockets={has_socket_residuals} on display :{display}, cleaning up...")
logger.info(
f"Found residual processes={has_process_residuals} sockets={has_socket_residuals} on display :{display}, cleaning up..."
)
if has_process_residuals:
patterns = [
f'xpra.*:{display}',
f'Xvfb.*:{display}',
f'wireshark.*:{display}',
f'pulseaudio.*display=:{display}'
f"xpra.*:{display}",
f"Xvfb.*:{display}",
f"wireshark.*:{display}",
f"pulseaudio.*display=:{display}",
]
await self._kill_process_tree_batch(container_id, patterns)
if has_socket_residuals:
@@ -848,29 +818,24 @@ class WebWiresharkManager:
xpra_cmd = [
"XPRA_CLIENT_CAN_SHUTDOWN=false",
"xpra", "start", f":{display}",
"xpra",
"start",
f":{display}",
'--xvfb="Xvfb -screen 0 1920x1080x24 +extension RANDR"',
"--html=off",
f"--bind-ws=0.0.0.0:{port}",
f"--session-name={session_name}",
"--daemon=yes",
"--dbus-launch=no",
"--resize-display=yes"
"--resize-display=yes",
]
# Parallel execution: get container info + start xpra
container_info_task = asyncio.create_task(
self.docker.get_container(container_name)
)
xpra_start_task = asyncio.create_task(
self._exec_in_container(container_id, " ".join(xpra_cmd))
)
container_info_task = asyncio.create_task(self.docker.get_container(container_name))
xpra_start_task = asyncio.create_task(self._exec_in_container(container_id, " ".join(xpra_cmd)))
# Wait for both tasks to complete
container, (returncode, stdout, stderr) = await asyncio.gather(
container_info_task,
xpra_start_task
)
container, (returncode, stdout, stderr) = await asyncio.gather(container_info_task, xpra_start_task)
# Check xpra start result
if returncode != 0:
@@ -901,10 +866,14 @@ class WebWiresharkManager:
# Execute Wireshark command without waiting for completion
await asyncio.create_subprocess_exec(
"docker", "exec", container_id,
"bash", "-c", wireshark_cmd,
"docker",
"exec",
container_id,
"bash",
"-c",
wireshark_cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
stderr=asyncio.subprocess.PIPE,
)
# Don't wait - Wireshark runs in background
@@ -918,7 +887,7 @@ class WebWiresharkManager:
"container_name": container_name,
"container_id": container_id,
"session_name": session_name,
"capture_stream_url": capture_stream_url
"capture_stream_url": capture_stream_url,
}
logger.info(f"Web Wireshark session started successfully: {result['ws_url']}")
@@ -947,11 +916,11 @@ class WebWiresharkManager:
# This ensures child processes are properly terminated, not left as zombies
logger.info(f"Stopping all processes on display :{display}")
patterns = [
f'xpra.*:{display}',
f'Xvfb.*:{display}',
f'Xvfb-for-Xpra-{display}',
f'wireshark.*:{display}',
f'pulseaudio.*display=:{display}'
f"xpra.*:{display}",
f"Xvfb.*:{display}",
f"Xvfb-for-Xpra-{display}",
f"wireshark.*:{display}",
f"pulseaudio.*display=:{display}",
]
await self._kill_process_tree_batch(container["Id"], patterns)
await self._cleanup_x_lock(container["Id"], display)
@@ -971,7 +940,7 @@ class WebWiresharkManager:
memory: str = "2g",
memory_swap: str = None,
cpus: float = 1.0,
pids_limit: int = 1000
pids_limit: int = 1000,
):
"""Restart Web Wireshark session.
@@ -1001,7 +970,7 @@ class WebWiresharkManager:
memory=memory,
memory_swap=memory_swap,
cpus=cpus,
pids_limit=pids_limit
pids_limit=pids_limit,
)
async def stop_all_sessions(self, project_id: str):
@@ -1021,12 +990,7 @@ class WebWiresharkManager:
# Kill all wireshark, xpra and Xvfb processes for link sessions
# This ensures clean removal of all session processes
patterns = [
"xpra.*--session-name=link-",
"Xvfb-for-Xpra-",
"wireshark.*display :",
"pulseaudio.*display :"
]
patterns = ["xpra.*--session-name=link-", "Xvfb-for-Xpra-", "wireshark.*display :", "pulseaudio.*display :"]
await self._kill_process_tree_batch(container["Id"], patterns)
logger.info(f"All Web Wireshark sessions stopped for project {project_id}")
@@ -62,11 +62,7 @@ def check_docker():
# Check if Docker daemon is running
try:
result = subprocess.run(
["docker", "info"],
capture_output=True,
text=True
)
result = subprocess.run(["docker", "info"], capture_output=True, text=True)
if result.returncode != 0:
print("Error: Docker daemon is not running", file=sys.stderr)
print("Please start Docker and try again", file=sys.stderr)
@@ -82,11 +78,7 @@ def image_exists():
"""Check if the Docker image already exists."""
try:
result = subprocess.run(
["docker", "image", "inspect", DOCKER_IMAGE],
capture_output=True,
text=True
)
result = subprocess.run(["docker", "image", "inspect", DOCKER_IMAGE], capture_output=True, text=True)
return result.returncode == 0
except Exception:
return False
@@ -115,11 +107,7 @@ def pull_image():
print(f"Pulling Docker image: {DOCKER_IMAGE}")
print("-" * 60)
result = subprocess.run(
["docker", "pull", DOCKER_IMAGE],
capture_output=True,
text=True
)
result = subprocess.run(["docker", "pull", DOCKER_IMAGE], capture_output=True, text=True)
# Store output for network error detection
pull_output = result.stdout + result.stderr
@@ -140,33 +128,17 @@ def build_image(dockerfile_path):
print("-" * 60)
result = subprocess.run(
["docker", "build", "-t", DOCKER_IMAGE, "-f", dockerfile_path, "."],
cwd=dockerfile_dir,
pass_fds=(1, 2)
["docker", "build", "-t", DOCKER_IMAGE, "-f", dockerfile_path, "."], cwd=dockerfile_dir, pass_fds=(1, 2)
)
return result.returncode == 0
def main():
parser = argparse.ArgumentParser(
description="Setup Web Wireshark Docker image for GNS3"
)
parser.add_argument(
"--force",
action="store_true",
help="Force rebuild even if image exists"
)
parser.add_argument(
"--build-only",
action="store_true",
help="Only build locally, skip pull"
)
parser.add_argument(
"--pull-only",
action="store_true",
help="Only pull from registry, skip build"
)
parser = argparse.ArgumentParser(description="Setup Web Wireshark Docker image for GNS3")
parser.add_argument("--force", action="store_true", help="Force rebuild even if image exists")
parser.add_argument("--build-only", action="store_true", help="Only build locally, skip pull")
parser.add_argument("--pull-only", action="store_true", help="Only pull from registry, skip build")
args = parser.parse_args()
@@ -183,7 +155,7 @@ def main():
if image_exists() and not args.force:
print(f"Image {DOCKER_IMAGE} already exists.")
response = input("Do you want to rebuild it? [y/N]: ").strip().lower()
if response != 'y':
if response != "y":
print("Setup cancelled.")
sys.exit(0)
print()
+8 -20
View File
@@ -30,12 +30,7 @@ async def collect_webwireshark_stats(projects: List) -> Dict:
"""
from .manager import WebWiresharkManager
stats = {
"total_containers": 0,
"running_containers": 0,
"active_sessions": 0,
"containers": []
}
stats = {"total_containers": 0, "running_containers": 0, "active_sessions": 0, "containers": []}
# Create a single manager instance and reuse it
manager = WebWiresharkManager()
@@ -70,15 +65,14 @@ async def collect_webwireshark_stats(projects: List) -> Dict:
pids_limit = host_config.get("PidsLimit", 0)
# Count active capture sessions
active_sessions = sum(
1 for link in project.links.values()
if getattr(link, "capturing", False)
)
active_sessions = sum(1 for link in project.links.values() if getattr(link, "capturing", False))
stats["active_sessions"] += active_sessions
container_info["active_sessions"] = active_sessions
# Add resource limits
container_info["memory_limit"] = f"{memory_limit / (1024**3):.1f} GB" if memory_limit > 0 else "unlimited"
container_info["memory_limit"] = (
f"{memory_limit / (1024**3):.1f} GB" if memory_limit > 0 else "unlimited"
)
container_info["cpu_limit"] = f"{cpu_quota / 1000000000:.1f}" if cpu_quota > 0 else "unlimited"
container_info["pids_limit"] = pids_limit if pids_limit > 0 else "unlimited"
@@ -111,21 +105,15 @@ async def _get_container_resource_stats(container_id: str) -> Optional[Dict]:
"""
try:
result = subprocess.run(
["docker", "stats", "--no-stream", "--format",
"{{.MemUsage}}\t{{.CPUPerc}}\t{{.PIDs}}",
container_id],
["docker", "stats", "--no-stream", "--format", "{{.MemUsage}}\t{{.CPUPerc}}\t{{.PIDs}}", container_id],
capture_output=True,
text=True,
timeout=2
timeout=2,
)
if result.returncode == 0:
parts = result.stdout.strip().split("\t")
if len(parts) >= 3:
return {
"memory": parts[0],
"cpu": parts[1],
"pids": int(parts[2])
}
return {"memory": parts[0], "cpu": parts[1], "pids": int(parts[2])}
except subprocess.TimeoutExpired:
logger.debug(f"Docker stats timeout for container {container_id[:12]}")
except Exception as e: