Add comprehensive multi-vendor support for GNS3 network automation,
including a custom Netmiko driver for Huawei CloudEngine devices.
Features:
- Custom HuaweiTelnetCE driver for GNS3 emulation (no authentication)
- Auto-commit before exit to prevent [Y/N/C] prompts
- Dynamic device type detection from GNS3 node tags
- Support for both Cisco IOS and Huawei devices
- Proper VRP command handling (system-view, return confirmation)
Implementation:
- New package: utils/custom_netmiko/
- huawei_ce.py: Huawei CloudEngine driver
- tests/test_huawei_ce.py: Unit tests (9/9 passing)
- README.md: Driver development guide
- Updated tools for multi-vendor support:
- display_tools_nornir.py: Dynamic group generation
- config_tools_nornir.py: Multi-vendor config commands
- get_gns3_device_port.py: Device port extraction
- Documentation: multi-vendor-device-support.md
Limitations:
- huawei_telnet_ce driver requires devices without authentication
- For devices with username/password, use standard huawei_telnet driver
Co-Authored-By: Yue Guobin <yueguobin@outlook.com>"
14 KiB
Multi-Vendor Network Device Support
Overview
GNS3-Copilot supports network devices from multiple vendors through Netmiko and Nornir integration. The system includes a custom Netmiko driver for Huawei devices in GNS3 emulation environments and supports dynamic device type detection.
Supported Vendors
| Vendor | Platform | Device Type | Protocol | Status |
|---|---|---|---|---|
| Cisco | cisco_ios |
cisco_ios_telnet |
Telnet | ✅ Tested |
| Huawei | huawei |
huawei_telnet_ce |
Telnet | ✅ Tested (Custom Driver) |
Custom Huawei Driver (HuaweiTelnetCE)
Problem Statement
GNS3-emulated Huawei devices connect via console without requiring authentication. Standard Netmiko drivers attempt username/password authentication, causing connection timeouts.
Standard Driver Behavior:
Telnet Connection → Wait for username prompt → Send username → Wait for password → Send password → Access
^ Times out after 20 seconds
GNS3 Huawei Device:
Telnet Connection → Direct access to command line (no login prompts)
<HUAWEI>
Solution: Custom Driver Architecture
BaseConnection (Netmiko base class)
↓
CiscoBaseConnection (Cisco-style base class)
↓
HuaweiBase (Huawei device base class) ← Inherits VRP support
↓
HuaweiTelnetCE (Custom GNS3 driver) ← Overrides telnet_login only
Why Inherit from HuaweiBase?
- ✅ Built-in VRP (Versatile Routing Platform) command handling
- ✅ Huawei-specific configuration mode (
system-view) - ✅ Huawei prompt patterns (
<...>,[...]) - ✅ Huawei paging disable (
screen-length 0 temporary) - ✅ Minimal code changes - only override authentication
HuaweiTelnetCE Implementation
Location
gns3server/agent/gns3_copilot/utils/custom_netmiko/huawei_ce.py
Package Structure:
custom_netmiko/
├── __init__.py # Package initialization, auto-registers all drivers
├── huawei_ce.py # Huawei CloudEngine custom driver
├── README.md # Driver development guide
└── tests/ # Unit tests
├── __init__.py
└── test_huawei_ce.py # Huawei CE driver tests
Key Features
-
Skip Authentication
- Directly detect Huawei prompt patterns
- No username/password prompts
- Connection ready in < 1 second
-
VRP Prompt Recognition
User view: <HUAWEI> System view: [HUAWEI] Interface: [HUAWEI-GigabitEthernet0/0/1] -
Automatic Confirmation Handling
- Detects and responds to
[y/n]prompts - Example:
returncommand asks "Return to user view? [y/n]:" - Automatically sends
yto confirm
- Detects and responds to
-
Proper Output Collection
- Uses Netmiko's
read_channel_timing()for reliable output - Waits for command completion (2s no new data = done)
- 30-second absolute timeout prevents hanging
- Uses Netmiko's
-
Auto-Commit Before Exit
- Automatically sends
commitcommand before exiting config mode - Prevents "Uncommitted configurations [Y/N/C]" prompt
- Ensures configuration changes are saved
- Automatically sends
Limitations
Authentication Requirement:
- The
huawei_telnet_cedriver is designed for GNS3 devices without authentication - If your Huawei device has been configured with a username/password:
- Option 1: Use the standard
huawei_telnetdriver (requires username/password) - Option 2: Remove authentication from the device for GNS3 testing
- Option 1: Use the standard
- The driver does not currently auto-detect authentication requirements
When to Use Each Driver:
| Scenario | Use Driver | Requires Credentials? |
|---|---|---|
| GNS3 Huawei (fresh, no auth) | huawei_telnet_ce |
❌ No |
| GNS3 Huawei (configured with username/password) | huawei_telnet |
✅ Yes |
| Real Huawei hardware | huawei_telnet |
✅ Yes |
Method Overrides
1. telnet_login - Skip Authentication
def telnet_login(self, pri_prompt_terminator=r"<\S+>|>\s*$",
alt_prompt_terminator=r"\[\S+\]", ...) -> str:
# Clear buffer
self.read_channel()
# Send returns until prompt detected
for i in range(max_loops):
self.write_channel(self.RETURN)
output = self.read_channel()
# Check for Huawei prompts
if re.search(pri_prompt_terminator, output):
return output # Success!
return output # Best effort
2. send_config_set - Configuration Commands
def send_config_set(self, config_commands, **kwargs) -> str:
# Enter config mode
output += self.config_mode(config_command="system-view")
# Send all commands
for cmd in config_commands:
self.write_channel(f"{cmd}{self.RETURN}")
time.sleep(delay_factor * 0.05)
# Collect output using Netmiko standard method
output += self.read_channel_timing(read_timeout=30, last_read=2.0)
# Auto-commit before exit (prevents [Y/N/C] prompt)
self.write_channel(f"commit{self.RETURN}")
time.sleep(0.5 * self.global_delay_factor)
output += self.read_channel()
# Exit config mode
output += self.exit_config_mode()
return output
3. exit_config_mode - Handle Confirmation
def exit_config_mode(self, exit_config="return", pattern=r"<\S+>|>\s*$") -> str:
self.write_channel(f"return{self.RETURN}")
# Look for confirmation prompt
for _ in range(20):
new_output = self.read_channel()
if re.search(r"\[y/n\]", new_output):
self.write_channel(f"y{self.RETURN}") # Auto-confirm
if re.search(pattern, new_output):
return output # Back to user view
return output
Device Type Registration
The custom driver must be registered with Netmiko's global mappings:
def register_custom_device_type() -> None:
import importlib
sd = importlib.import_module("netmiko.ssh_dispatcher")
# Register in CLASS_MAPPER (for ConnectHandler)
sd.CLASS_MAPPER["huawei_telnet_ce"] = HuaweiTelnetCE
sd.CLASS_MAPPER["huawei_ce"] = HuaweiTelnetCE
# Register in CLASS_MAPPER_BASE (for base class definitions)
sd.CLASS_MAPPER_BASE["huawei_telnet_ce"] = HuaweiTelnetCE
sd.CLASS_MAPPER_BASE["huawei_ce"] = HuaweiTelnetCE
# CRITICAL: Rebuild static lists
sd.platforms = list(sd.CLASS_MAPPER.keys())
sd.platforms.sort()
sd.telnet_platforms = [x for x in sd.platforms if "telnet" in x]
Important: Static List Problem
ssh_dispatcher.platformsis computed at module import time- Modifying
CLASS_MAPPERdoesn't automatically updateplatforms - Must manually rebuild the list after registration
Auto-Registration
# Automatically runs on module import
try:
register_custom_device_type()
except Exception as e:
logger.warning(f"Failed to register custom device type: {e}")
Dynamic Device Type Detection
GNS3 Node Tags
Device type and platform are extracted from GNS3 node tags:
device_type:huawei_telnet_ce → Netmiko device type (precise)
platform:huawei → Nornir platform (high-level)
Tag Examples:
| Vendor | Device Type Tag | Platform Tag |
|---|---|---|
| Cisco IOS | device_type:cisco_ios_telnet |
platform:cisco_ios |
| Huawei CE | device_type:huawei_telnet_ce |
platform:huawei |
Automatic Group Generation
Nornir groups are dynamically generated based on device type:
# From get_gns3_device_port.py
if device_type and "_telnet" in device_type:
group_name = f"{platform}_telnet" # e.g., "huawei_telnet"
else:
group_name = platform # e.g., "cisco_ios"
hosts_data[device_name] = {
"port": console_port,
"groups": [group_name],
"device_type": device_type,
"platform": platform,
}
Why Dynamic Groups?
- Single Nornir instance can handle multiple vendors
- Each device gets correct connection parameters
- Vendor-specific commands work automatically
Usage Examples
Direct Netmiko Usage
Huawei Device (Custom Driver):
from netmiko import ConnectHandler
from gns3server.agent.gns3_copilot.utils import custom_netmiko
# Custom driver auto-registers on import
device = {
"device_type": "huawei_telnet_ce",
"host": "127.0.0.1",
"port": 5000,
# No username/password needed!
}
with ConnectHandler(**device) as conn:
# Execute display command
output = conn.send_command("display version")
# Execute configuration commands
config = [
"interface GE1/0/1",
"description Uplink-to-Core",
"undo shutdown"
]
output = conn.send_config_set(config)
Cisco IOS Device (Standard Driver):
from netmiko import ConnectHandler
device = {
"device_type": "cisco_ios_telnet",
"host": "127.0.0.1",
"port": 5001,
"username": "cisco",
"password": "cisco",
}
with ConnectHandler(**device) as conn:
output = conn.send_command("show version")
config = ["interface GigabitEthernet0/0", "description Test"]
output = conn.send_config_set(config)
Nornir Multi-Vendor Automation
from nornir import InitNornir
from gns3server.agent.gns3_copilot.utils import custom_netmiko
# Auto-register custom driver (happens automatically on import)
from gns3server.agent.gns3_copilot.utils.custom_netmiko import huawei_ce
huawei_ce.register_custom_device_type()
# Initialize Nornir with mixed-vendor inventory
inventory = {
"options": {
"hosts": {
"huawei-sw1": {
"hostname": "127.0.0.1",
"port": 5001,
"platform": "huawei",
"device_type": "huawei_telnet_ce",
"groups": ["huawei_telnet"]
},
"cisco-r1": {
"hostname": "127.0.0.1",
"port": 5002,
"platform": "cisco_ios",
"device_type": "cisco_ios_telnet",
"groups": ["cisco_ios_telnet"]
}
},
"groups": {
"huawei_telnet": {
"platform": "huawei",
"device_type": "huawei_telnet_ce"
},
"cisco_ios_telnet": {
"platform": "cisco_ios",
"device_type": "cisco_ios_telnet"
}
}
}
}
nr = InitNornir(inventory=inventory)
# Execute commands on all devices (multi-vendor)
result = nr.run(task=send_commands, commands=["display version"])
# Each device gets vendor-specific command handling
GNS3 Copilot Tool Usage
from gns3server.agent.gns3_copilot.tools_v2 import DisplayToolNornir
tool = DisplayToolNornir()
result = tool._run(json.dumps({
"device_names": ["huawei-sw1", "cisco-r1"],
"commands": ["display version", "show version"],
"project_id": "project-uuid"
}))
# Returns:
# {
# "huawei-sw1": {
# "display version": "<Huawei output>",
# "status": "success"
# },
# "cisco-r1": {
# "show version": "<Cisco output>",
# "status": "success"
# }
# }
Module Structure
gns3server/agent/gns3_copilot/
├── utils/
│ ├── custom_netmiko/ # Custom Netmiko drivers package
│ │ ├── __init__.py # Package initialization
│ │ ├── huawei_ce.py # Huawei CloudEngine driver
│ │ ├── README.md # Driver development guide
│ │ └── tests/ # Unit tests
│ │ ├── __init__.py
│ │ └── test_huawei_ce.py # Huawei CE driver tests
│ └── get_gns3_device_port.py # Device port extraction
├── tools_v2/
│ ├── display_tools_nornir.py # Multi-vendor display commands
│ └── config_tools_nornir.py # Multi-vendor config commands
Unit Testing
Test Coverage
# test_netmiko_custom.py
class TestHuaweiTelnetCEDriver(unittest.TestCase):
def test_device_type_registered(self):
"""Verify huawei_telnet_ce is in Netmiko CLASS_MAPPER"""
from netmiko.ssh_dispatcher import CLASS_MAPPER
self.assertIn("huawei_telnet_ce", CLASS_MAPPER)
def test_inheritance_from_huawei_base(self):
"""Verify inherits from HuaweiBase"""
from netmiko.huawei.huawei import HuaweiBase
self.assertTrue(issubclass(HuaweiTelnetCE, HuaweiBase))
def test_vrp_methods_available(self):
"""Verify VRP-specific methods are available"""
methods = ["config_mode", "check_config_mode", "exit_config_mode"]
for method in methods:
self.assertTrue(hasattr(HuaweiTelnetCE, method))
Running Tests:
source venv/bin/activate
python gns3server/agent/gns3_copilot/utils/custom_netmiko/tests/test_huawei_ce.py
Current Test Status: ✅ All 9 tests passing
Platform vs Device Type
Key Concepts
Platform (Nornir):
- High-level vendor identifier
- Used for inventory grouping
- Examples:
huawei,cisco_ios
Device Type (Netmiko):
- Precise driver type
- Includes protocol information
- Examples:
huawei_telnet_ce,cisco_ios_telnet
Mapping
| Platform | Device Type | Notes |
|---|---|---|
huawei |
huawei_telnet_ce |
Custom driver for GNS3 |
cisco_ios |
cisco_ios_telnet |
Standard Netmiko driver |
Related Documentation
- Custom Netmiko README - Driver development guide
- Node Control Tools - Device lifecycle management
- Command Security - Command filtering and validation
- Chat API - Session management and SSE
References
Implementation Date: 2026-03-12
Status: ✅ Implemented - Custom Huawei driver for GNS3 emulation, multi-vendor support with Cisco IOS and Huawei tested
Unit Tests: ✅ 9/9 passing