mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-10-28 03:02:22 +02:00
eat(copilot): add dynamic wait time calculation for node startup
Optimize GNS3StartNodeTool with device-type-aware wait time calculation
to significantly reduce startup time for fast devices (VPCS, IOU).
Changes:
- Add NODE_STARTUP_TIME configuration
* VPCS: 10s base + 2s per additional node
* IOU: 20s base + 3s per additional node
* Other devices: 120s base + 10s per additional node (conservative)
- Add calculate_startup_time() function
* Detects device types via node.node_type
* Uses fast startup time if all nodes are VPCS/IOU
* Uses conservative time if any slow device present
* Logs selected strategy and detected types
- Optimize GNS3StartNodeTool._run() method
* Retrieve node info (including node_type) before starting
* Calculate wait time based on detected device types
* Send start commands after info retrieval
* Use calculated wait time for progress bar
* Reuse collected node objects for status retrieval
Performance improvements:
- 1 VPCS node: 140s → 10s (93% faster)
- 5 VPCS nodes: 180s → 18s (90% faster)
- 1 IOU node: 140s → 20s (86% faster)
- 5 IOU nodes: 180s → 32s (82% faster)
- Mixed VPCS/IOU: 180s → 28s (84% faster)
Documentation:
- Update node-control-tools.md with dynamic wait time strategy
- Add device type comparison table
- Document performance improvements
- Update changelog
Code quality:
- All comments in English
- flake8 check passed
- mypy check passed
This commit is contained in:
parent
df8338401e
commit
a946ef2d69
@ -234,7 +234,7 @@ The following built-in utility templates are excluded as they are not actual net
|
|||||||
|
|
||||||
**Tool Name:** `start_gns3_node`
|
**Tool Name:** `start_gns3_node`
|
||||||
|
|
||||||
**Description:** Starts one or multiple nodes in a GNS3 project with progress tracking and status monitoring.
|
**Description:** Starts one or multiple nodes in a GNS3 project with progress tracking and status monitoring. Features dynamic wait time calculation based on device types for optimal performance.
|
||||||
|
|
||||||
**Input:**
|
**Input:**
|
||||||
```json
|
```json
|
||||||
@ -263,12 +263,46 @@ The following built-in utility templates are excluded as they are not actual net
|
|||||||
- Progress bar with visual feedback
|
- Progress bar with visual feedback
|
||||||
- Automatic status verification
|
- Automatic status verification
|
||||||
- Comprehensive error handling
|
- Comprehensive error handling
|
||||||
- ~140s base wait time + 10s per additional node
|
- **Dynamic wait time based on device types** 🆕
|
||||||
|
|
||||||
|
**Dynamic Wait Time Strategy:**
|
||||||
|
|
||||||
|
The tool automatically detects node types and calculates optimal wait times:
|
||||||
|
|
||||||
|
| Device Type | Base Time | Per Additional Node | Example (5 nodes) |
|
||||||
|
|-------------|-----------|-------------------|-------------------|
|
||||||
|
| **VPCS** | 10s | +2s | 18s total |
|
||||||
|
| **IOU** | 20s | +3s | 32s total |
|
||||||
|
| **Mixed VPCS/IOU** | 20s | +2s | 28s total |
|
||||||
|
| **QEMU/Dynamips/Docker/VMs** | 120s | +10s | 160s total |
|
||||||
|
| **Mixed (any slow device)** | 120s | +10s | 160s total |
|
||||||
|
|
||||||
|
**How It Works:**
|
||||||
|
1. Retrieves node information including `node_type`
|
||||||
|
2. Checks if all nodes are fast devices (VPCS/IOU only)
|
||||||
|
3. If all fast: Uses max(10s, 20s) base + 2s per extra node
|
||||||
|
4. If any slow device present: Uses 120s base + 10s per extra node
|
||||||
|
5. Logs the detected device types and chosen strategy
|
||||||
|
|
||||||
|
**Performance Improvements:**
|
||||||
|
- ⚡ **1 VPCS node**: 140s → 10s (93% faster)
|
||||||
|
- ⚡ **5 VPCS nodes**: 180s → 18s (90% faster)
|
||||||
|
- ⚡ **1 IOU node**: 140s → 20s (86% faster)
|
||||||
|
- ⚡ **5 IOU nodes**: 180s → 32s (82% faster)
|
||||||
|
- ⚡ **Mixed VPCS/IOU**: 180s → 28s (84% faster)
|
||||||
|
|
||||||
**Use Cases:**
|
**Use Cases:**
|
||||||
- Automated lab deployment
|
- Automated lab deployment
|
||||||
- Multi-node topology initialization
|
- Multi-node topology initialization
|
||||||
- Lab startup automation
|
- Lab startup automation
|
||||||
|
- Fast VPCS/IOU lab deployment
|
||||||
|
|
||||||
|
**Implementation Details:**
|
||||||
|
- Retrieves node type via `node.get()` before starting
|
||||||
|
- Calculates wait time using `calculate_startup_time()` function
|
||||||
|
- Uses device-specific `NODE_STARTUP_TIME` configuration
|
||||||
|
- Logs device types and selected wait strategy
|
||||||
|
- Progress bar displays calculated wait time
|
||||||
|
|
||||||
### GNS3StopNodeTool
|
### GNS3StopNodeTool
|
||||||
|
|
||||||
@ -907,12 +941,20 @@ logger.info("Suspend command sent for node %s (%s)", node_id, node.name)
|
|||||||
|
|
||||||
_Implementation Date: 2026-03-12_
|
_Implementation Date: 2026-03-12_
|
||||||
|
|
||||||
_Last Updated: 2026-03-14 (Added template filtering to exclude built-in utility templates)_
|
_Last Updated: 2026-03-14 (Added dynamic wait time calculation based on device types)_
|
||||||
|
|
||||||
_Status: ✅ Implemented - Topology management tools available in both modes. Full lifecycle management (start/stop/suspend) available in Lab Automation Assistant Mode_
|
_Status: ✅ Implemented - Topology management tools available in both modes. Full lifecycle management (start/stop/suspend) available in Lab Automation Assistant Mode_
|
||||||
|
|
||||||
_Changelog:_
|
_Changelog:_
|
||||||
- **2026-03-14**: Added template filtering
|
- **2026-03-14 (Evening)**: Added dynamic wait time calculation
|
||||||
|
- `GNS3StartNodeTool` now calculates optimal wait times based on device types
|
||||||
|
- Fast devices (VPCS: 10s, IOU: 20s) start much faster than before
|
||||||
|
- Slow devices (QEMU, Dynamips, etc.) use conservative 120s base time
|
||||||
|
- Performance improvements: 82-93% faster for VPCS/IOU labs
|
||||||
|
- Automatic device type detection via `node.node_type`
|
||||||
|
- Logs selected strategy and detected device types
|
||||||
|
|
||||||
|
- **2026-03-14 (Morning)**: Added template filtering
|
||||||
- `GNS3TemplateTool` now filters out built-in utility templates (cloud, nat, ethernet_hub, ethernet_switch, frame_relay_switch, atm_switch)
|
- `GNS3TemplateTool` now filters out built-in utility templates (cloud, nat, ethernet_hub, ethernet_switch, frame_relay_switch, atm_switch)
|
||||||
- Focuses on network devices suitable for lab configuration
|
- Focuses on network devices suitable for lab configuration
|
||||||
- Logs filtered count for transparency
|
- Logs filtered count for transparency
|
||||||
|
|||||||
@ -45,6 +45,69 @@ from gns3server.agent.gns3_copilot.gns3_client import get_gns3_connector
|
|||||||
# Configure logging
|
# Configure logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Node startup time configuration by device type
|
||||||
|
# Based on typical boot times for different emulators
|
||||||
|
NODE_STARTUP_TIME = {
|
||||||
|
"vpcs": {"base": 10, "extra_per_node": 2}, # VPCS: Very fast startup
|
||||||
|
"iou": {"base": 20, "extra_per_node": 3}, # IOU: Fast startup
|
||||||
|
"default": {"base": 120, "extra_per_node": 10}, # Other devices: Conservative time
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_startup_time(nodes: list) -> int:
|
||||||
|
"""
|
||||||
|
Calculate startup wait time based on node types.
|
||||||
|
|
||||||
|
Strategy:
|
||||||
|
- If all nodes are fast devices (VPCS/IOU): use fast startup time
|
||||||
|
- If any node is a slow device: use conservative startup time
|
||||||
|
|
||||||
|
Args:
|
||||||
|
nodes: List of node objects with node_type attribute
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Calculated wait time in seconds
|
||||||
|
"""
|
||||||
|
if not nodes:
|
||||||
|
return 60 # Default: 60 seconds for empty list
|
||||||
|
|
||||||
|
# Get all node types
|
||||||
|
node_types = [getattr(node, "node_type", "default") for node in nodes]
|
||||||
|
|
||||||
|
# Check if all nodes are fast startup devices (VPCS or IOU)
|
||||||
|
fast_types = {"vpcs", "iou"}
|
||||||
|
all_fast = all(node_type in fast_types for node_type in node_types)
|
||||||
|
|
||||||
|
if all_fast:
|
||||||
|
# Use fast startup time: base + (count - 1) * extra_per_node
|
||||||
|
# Use the largest base time among the fast devices
|
||||||
|
max_fast_base = max(
|
||||||
|
NODE_STARTUP_TIME[nt]["base"]
|
||||||
|
for nt in node_types if nt in fast_types
|
||||||
|
)
|
||||||
|
# Use the smallest extra_per_node among the fast devices
|
||||||
|
min_fast_extra = min(
|
||||||
|
NODE_STARTUP_TIME[nt]["extra_per_node"]
|
||||||
|
for nt in node_types if nt in fast_types
|
||||||
|
)
|
||||||
|
total_time = max_fast_base + (len(nodes) - 1) * min_fast_extra
|
||||||
|
logger.info(
|
||||||
|
"All fast devices detected (%s), using fast startup time: %ds",
|
||||||
|
node_types,
|
||||||
|
total_time
|
||||||
|
)
|
||||||
|
return total_time
|
||||||
|
else:
|
||||||
|
# Use conservative startup time for mixed or slow devices
|
||||||
|
config = NODE_STARTUP_TIME["default"]
|
||||||
|
total_time = config["base"] + (len(nodes) - 1) * config["extra_per_node"]
|
||||||
|
logger.info(
|
||||||
|
"Mixed or slow devices detected (%s), using conservative startup time: %ds",
|
||||||
|
node_types,
|
||||||
|
total_time
|
||||||
|
)
|
||||||
|
return total_time
|
||||||
|
|
||||||
|
|
||||||
def show_progress_bar(
|
def show_progress_bar(
|
||||||
duration: int = 120, interval: int = 1, node_count: int = 1
|
duration: int = 120, interval: int = 1, node_count: int = 1
|
||||||
@ -145,12 +208,13 @@ class GNS3StartNodeTool(BaseTool):
|
|||||||
"Please check your configuration."
|
"Please check your configuration."
|
||||||
}
|
}
|
||||||
|
|
||||||
# First loop: Send start commands for all nodes
|
# First loop: Get node info and send start commands for all nodes
|
||||||
logger.info(
|
logger.info(
|
||||||
"Sending start commands for %d nodes in project %s...",
|
"Retrieving node info for %d nodes in project %s...",
|
||||||
len(node_ids),
|
len(node_ids),
|
||||||
project_id,
|
project_id,
|
||||||
)
|
)
|
||||||
|
nodes = []
|
||||||
for node_id in node_ids:
|
for node_id in node_ids:
|
||||||
try:
|
try:
|
||||||
node = Node(
|
node = Node(
|
||||||
@ -158,11 +222,16 @@ class GNS3StartNodeTool(BaseTool):
|
|||||||
node_id=node_id,
|
node_id=node_id,
|
||||||
connector=gns3_server,
|
connector=gns3_server,
|
||||||
)
|
)
|
||||||
# Verify node exists
|
# Get node info (including node_type)
|
||||||
node.get()
|
node.get()
|
||||||
if node.node_id:
|
if node.node_id:
|
||||||
node.start()
|
nodes.append(node)
|
||||||
logger.info("Start command sent for node %s", node_id)
|
logger.info(
|
||||||
|
"Node %s (%s) type: %s",
|
||||||
|
node_id,
|
||||||
|
node.name,
|
||||||
|
node.node_type,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
"Node %s not found in project %s",
|
"Node %s not found in project %s",
|
||||||
@ -171,31 +240,41 @@ class GNS3StartNodeTool(BaseTool):
|
|||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
"Failed to send start command for node %s: %s",
|
"Failed to get node info for %s: %s",
|
||||||
node_id,
|
node_id,
|
||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Progress bar duration: 140s base + 10s per extra node
|
# Calculate startup time based on node types
|
||||||
base_duration = 140
|
wait_time = calculate_startup_time(nodes)
|
||||||
extra_duration = max(0, len(node_ids) - 1) * 10
|
|
||||||
total_duration = base_duration + extra_duration
|
|
||||||
|
|
||||||
# Show progress bar
|
# Send start commands for all nodes
|
||||||
|
logger.info(
|
||||||
|
"Sending start commands for %d nodes in project %s...",
|
||||||
|
len(nodes),
|
||||||
|
project_id,
|
||||||
|
)
|
||||||
|
for node in nodes:
|
||||||
|
try:
|
||||||
|
node.start()
|
||||||
|
logger.info("Start command sent for node %s", node.node_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
"Failed to send start command for node %s: %s",
|
||||||
|
node.node_id,
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show progress bar with calculated wait time
|
||||||
show_progress_bar(
|
show_progress_bar(
|
||||||
duration=total_duration, interval=1, node_count=len(node_ids)
|
duration=wait_time, interval=1, node_count=len(nodes)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Second loop: Get status for all nodes
|
# Second loop: Get status for all nodes
|
||||||
results = []
|
results = []
|
||||||
logger.info("Retrieving status for %d nodes...", len(node_ids))
|
logger.info("Retrieving status for %d nodes...", len(nodes))
|
||||||
for node_id in node_ids:
|
for node in nodes:
|
||||||
try:
|
try:
|
||||||
node = Node(
|
|
||||||
project_id=project_id,
|
|
||||||
node_id=node_id,
|
|
||||||
connector=gns3_server,
|
|
||||||
)
|
|
||||||
node.get() # Get latest status
|
node.get() # Get latest status
|
||||||
node_info = {
|
node_info = {
|
||||||
"node_id": node.node_id,
|
"node_id": node.node_id,
|
||||||
@ -205,14 +284,27 @@ class GNS3StartNodeTool(BaseTool):
|
|||||||
results.append(node_info)
|
results.append(node_info)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
"Failed to get status for node %s: %s", node_id, e
|
"Failed to get status for node %s: %s", node.node_id, e
|
||||||
)
|
)
|
||||||
|
results.append(
|
||||||
|
{
|
||||||
|
"node_id": node.node_id,
|
||||||
|
"name": getattr(node, "name", "N/A"),
|
||||||
|
"status": "error",
|
||||||
|
"error": str(e),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Handle nodes that failed to be retrieved initially
|
||||||
|
retrieved_node_ids = {node.node_id for node in nodes}
|
||||||
|
for node_id in node_ids:
|
||||||
|
if node_id not in retrieved_node_ids:
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
"node_id": node_id,
|
"node_id": node_id,
|
||||||
"name": "N/A",
|
"name": "N/A",
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"error": str(e),
|
"error": "Node not found during info retrieval",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user