refactor: remove old PacketCaptureTool, replaced by PacketAnalysisTool

- Delete packet_capture_tools.py (analyze_packets with packet_number)
- PacketAnalysisTool is more flexible and can do everything the old tool could
- Update tool registration to use only PacketAnalysisTool
- Cleaner, more unified packet analysis interface
This commit is contained in:
YueGuobin 2026-05-12 10:19:25 +08:00
parent 84b9742045
commit 2c5cfdbee8
No known key found for this signature in database
3 changed files with 0 additions and 311 deletions

View File

@ -94,7 +94,6 @@ from gns3server.agent.gns3_copilot.tools_v2 import GNS3SuspendNodeTool
from gns3server.agent.gns3_copilot.tools_v2 import GNS3TemplateTool
from gns3server.agent.gns3_copilot.tools_v2 import GNS3UpdateNodeNameTool
from gns3server.agent.gns3_copilot.tools_v2.vpcs_tools_netmiko import VPCSCommands
from gns3server.agent.gns3_copilot.tools_v2 import PacketCaptureTool
from gns3server.agent.gns3_copilot.tools_v2 import PacketAnalysisTool
from gns3server.agent.gns3_copilot.skills import DeviceSkillsTool
from gns3server.agent.gns3_copilot.skills import InjectionSkillsTool
@ -116,7 +115,6 @@ TEACHING_ASSISTANT_MODE_TOOLS = [
GNS3UpdateNodeNameTool(), # Update node name
ExecuteMultipleDeviceCommands(), # Execute show/display/debug commands
# (READ-ONLY)
PacketCaptureTool(), # Analyze packets from active capture
PacketAnalysisTool(), # Protocol-oriented packet analysis with tshark
DeviceSkillsTool(), # Get device-specific skills and command knowledge
]
@ -134,7 +132,6 @@ LAB_AUTOMATION_ASSISTANT_MODE_TOOLS = [
# (READ-ONLY)
ExecuteMultipleDeviceConfigCommands(), # Execute configuration commands
VPCSCommands(), # Execute VPCS commands using Netmiko
PacketCaptureTool(), # Analyze packets from active capture
PacketAnalysisTool(), # Protocol-oriented packet analysis with tshark
DeviceSkillsTool(), # Get device-specific skills and command knowledge
]

View File

@ -59,7 +59,6 @@ from .gns3_start_node import GNS3StartNodeTool
from .gns3_stop_node import GNS3StopNodeTool
from .gns3_suspend_node import GNS3SuspendNodeTool
from .gns3_update_node_name import GNS3UpdateNodeNameTool
from .packet_capture_tools import PacketCaptureTool
from .packet_analysis_tool import PacketAnalysisTool
# Dynamic version management
@ -86,7 +85,6 @@ __all__ = [
"GNS3SuspendNodeTool",
"GNS3UpdateNodeNameTool",
"GNS3TemplateTool",
"PacketCaptureTool",
"PacketAnalysisTool",
]

View File

@ -1,306 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
# GNS3-Copilot - AI-powered Network Lab Assistant for GNS3
#
# This file is part of GNS3-Copilot project.
#
# GNS3-Copilot is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GNS3-Copilot is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNS3-Copilot. If not, see <https://www.gnu.org/licenses/>.
#
# Copyright (C) 2025 Yue Guobin (岳国宾)
# Author: Yue Guobin (岳国宾)
#
# Project Home: https://github.com/yueguobin/gns3-copilot
#
"""
Packet Capture Analysis Tool
Analyzes specific packets from an active GNS3 capture using tshark.
Downloads the capture file from GNS3 server and returns detailed packet information.
"""
import logging
import os
import subprocess
import tempfile
from langchain.tools import BaseTool
from langchain_core.callbacks import CallbackManagerForToolRun
from gns3server.agent.gns3_copilot.gns3_client.context_helpers import (
get_current_jwt_token,
)
logger = logging.getLogger(__name__)
class PacketCaptureTool(BaseTool):
"""
LangChain tool for analyzing a specific packet from an active GNS3 capture.
This tool:
1. Downloads the capture file from GNS3 server via /capture/file endpoint
2. Saves it to a temporary file
3. Runs tshark with verbose output (-V) for the specified packet
4. Returns the complete packet structure
Input:
project_id (str, required): UUID of the GNS3 project
link_id (str, required): UUID of the link to analyze
packet_number (int, required): The packet number to analyze
Output:
str: Detailed packet information in text format
"""
name: str = "analyze_packets"
description: str = """
Analyze a specific packet from an active GNS3 capture using tshark.
Downloads the capture file from GNS3 server and returns detailed packet information.
Input (JSON format):
- project_id (str, required): UUID of the GNS3 project
- link_id (str, required): UUID of the link to analyze
- packet_number (int, required): The packet number to analyze
Output:
Complete packet structure in verbose text format
Example:
# Analyze packet #42
tool._run(
project_id="xxx",
link_id="yyy",
packet_number=42
)
"""
def _run(
self,
project_id: str,
link_id: str,
packet_number: int,
run_manager: CallbackManagerForToolRun | None = None,
) -> str:
"""
Analyze a specific packet from an active GNS3 capture.
Args:
project_id: UUID of the GNS3 project
link_id: UUID of the link to analyze
packet_number: The packet number to analyze
run_manager: LangChain run manager (unused)
Returns:
str: Detailed packet information
"""
logger.info(
f"PacketCaptureTool invoked: project_id={project_id}, "
f"link_id={link_id}, packet_number={packet_number}"
)
# Validate inputs
if not project_id:
return '{"error": "project_id is required"}'
if not link_id:
return '{"error": "link_id is required"}'
if packet_number is None or packet_number <= 0:
return '{"error": "packet_number must be a positive integer"}'
temp_file = None
try:
# Download capture file
temp_file = self._download_capture(project_id, link_id)
if not temp_file:
return '{"error": "Failed to download capture file"}'
# Check if file exists and has content
if not os.path.exists(temp_file):
return '{"error": "Capture file not found"}'
file_size = os.path.getsize(temp_file)
if file_size == 0:
return '{"error": "Capture file is empty, no packets captured yet"}'
logger.info(f"Capture file downloaded: {temp_file}, size={file_size} bytes")
# Run tshark verbose analysis for the specific packet
result = self._run_tshark_verbose(temp_file, packet_number)
return result
except Exception as e:
logger.error(f"PacketCaptureTool error: {e}", exc_info=True)
return f'{{"error": "Analysis failed: {str(e)}"}}'
finally:
# Clean up temp file
if temp_file and os.path.exists(temp_file):
try:
os.remove(temp_file)
logger.debug(f"Temporary file removed: {temp_file}")
except Exception as e:
logger.warning(f"Failed to remove temp file: {e}")
def _download_capture(self, project_id: str, link_id: str) -> str | None:
"""
Download capture file from GNS3 server.
Args:
project_id: Project UUID
link_id: Link UUID
Returns:
str: Path to temporary capture file, or None on failure
"""
jwt_token = get_current_jwt_token()
if not jwt_token:
logger.error("JWT token not found in context")
return None
# Detect GNS3 server URL
url = self._detect_gns3_url()
if not url:
return None
capture_url = f"{url}/v3/projects/{project_id}/links/{link_id}/capture/file"
logger.info(f"Downloading capture from: {capture_url}")
# Create temp file
temp_fd, temp_file = tempfile.mkstemp(suffix=".pcap", prefix="gns3_capture_")
os.close(temp_fd)
try:
import requests
headers = {"Authorization": f"Bearer {jwt_token}"}
# Use verify=False for HTTPS to skip certificate validation (for self-signed certs)
verify_cert = not capture_url.startswith("https://")
response = requests.get(
capture_url,
headers=headers,
stream=True,
timeout=30,
verify=verify_cert,
)
if response.status_code != 200:
logger.error(f"Failed to download capture: HTTP {response.status_code}")
os.remove(temp_file)
return None
# Write to temp file
with open(temp_file, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
logger.info(f"Capture file saved: {temp_file}, size={os.path.getsize(temp_file)} bytes")
return temp_file
except Exception as e:
logger.error(f"Failed to download capture: {e}", exc_info=True)
if os.path.exists(temp_file):
os.remove(temp_file)
return None
def _detect_gns3_url(self) -> str | None:
"""
Detect GNS3 server URL from Controller or Config.
Returns:
str: GNS3 server URL, or None on failure
"""
try:
from gns3server.controller import Controller
controller = Controller.instance()
local_compute = controller.get_compute("local")
url = f"{local_compute.protocol}://{local_compute.host}:{local_compute.port}"
logger.debug(f"Detected GNS3 URL from Controller: {url}")
return url
except Exception as e:
logger.debug(f"Cannot get URL from Controller: {e}")
try:
from gns3server.config import Config
server_config = Config.instance().settings.Server
url = f"{server_config.protocol.value}://{server_config.host}:{server_config.port}"
logger.debug(f"Detected GNS3 URL from Config: {url}")
return url
except Exception as e:
logger.debug(f"Cannot get URL from Config: {e}")
# Fallback default
default_url = "http://127.0.0.1:3080"
logger.warning(f"Using fallback default URL: {default_url}")
return default_url
def _run_tshark_verbose(self, pcap_file: str, packet_number: int) -> str:
"""
Run tshark verbose output for a specific packet.
Args:
pcap_file: Path to the capture file
packet_number: The packet number to analyze
Returns:
str: tshark verbose output
"""
# Build command: tshark -r <file> -Y "frame.number == N" -V
cmd = [
"tshark",
"-r", pcap_file,
"-Y", f"frame.number == {packet_number}",
"-V"
]
logger.info(f"Running tshark: {' '.join(cmd)}")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=30,
)
output = result.stdout
if result.stderr:
if "tshark:" in result.stderr.lower():
logger.warning(f"tshark stderr: {result.stderr}")
if not output.strip():
return f'No packet found with frame.number == {packet_number}'
logger.info(f"tshark output: {len(output)} characters")
return output
except subprocess.TimeoutExpired:
logger.error("tshark timeout after 30 seconds")
return '{"error": "tshark timeout after 30 seconds"}'
except FileNotFoundError:
logger.error("tshark not found. Please install tshark: apt install tshark")
return '{"error": "tshark not installed. Please install tshark: apt install tshark"}'
except Exception as e:
logger.error(f"tshark execution error: {e}", exc_info=True)
return f'{{"error": "tshark failed: {str(e)}"}}'
if __name__ == "__main__":
# Test the tool
tool = PacketCaptureTool()
print("Testing PacketCaptureTool...")
print("Note: Set project_id, link_id and packet_number to test with actual GNS3 capture")
print(tool.description)