gns3-server/docs/gns3-copilot/todo/config-templates-implementation-guide.md
YueGuobin e452f75ac3 docs: reorganize AI Copilot documentation structure
- Add README.md with documentation overview and structure guide
- Move implemented designs to docs/gns3-copilot/implemented/:
  - chat-api.md (from ai-chat-api-design.md)
  - llm-model-configs.md (from llm-model-configs-api.md)
  - command-security.md
  - context-window-management.md
- Add Jinja2 configuration template system design documents:
  - jinja2-config-templates-system.md
  - config-templates-implementation-guide.md
  - ai-prompting-for-config-templates.md
- Remove obsolete documents (acl-web-ui, Chinese RBAC doc)

This reorganization makes it clearer which features are implemented
vs planned, following the established documentation structure.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-10 23:20:24 +08:00

737 lines
17 KiB
Markdown

# Configuration Templates Implementation Guide
## Quick Start Examples
### Example 1: Configure OSPF on a Cisco Router
**User Request**:
```
"Configure OSPF on R1 with process ID 100, router-id 1.1.1.1.
Include network 192.168.1.0/24 in area 0 and 10.0.0.0/8 in area 1.
Make GigabitEthernet0/0 a passive interface."
```
**AI Should Generate** (structured JSON):
```json
{
"ospf": {
"enabled": true,
"process_id": 100,
"router_id": "1.1.1.1",
"networks": [
{
"address": "192.168.1.0",
"wildcard": "0.0.0.255",
"area": 0
},
{
"address": "10.0.0.0",
"wildcard": "0.255.255.255",
"area": 1
}
],
"passive_interfaces": ["GigabitEthernet0/0"]
}
}
```
**Agent Action**:
```python
# The agent calls the render tool with the structured data
result = render_device_config(
node_id="node-1",
vendor="cisco",
os_type="ios",
config_data=ai_output
)
```
**Rendered Configuration**:
```cisco
router ospf 100
router-id 1.1.1.1
network 192.168.1.0 mask 0.0.0.255 area 0
network 10.0.0.0 mask 0.255.255.255 area 1
passive-interface GigabitEthernet0/0
!
```
---
### Example 2: Configure BGP with Multiple Neighbors
**User Request**:
```
"Configure BGP on R1 with AS 65001. Set up IBGP with R2 (10.0.0.2, AS 65001)
and EBGP with ISP (203.0.13.2, AS 65002). Advertise network 192.168.0.0/16.
Enable route-map INBOUND-FILTER on R2 inbound."
```
**AI Generates**:
```json
{
"bgp": {
"enabled": true,
"as_number": 65001,
"router_id": "1.1.1.1",
"log_neighbor_changes": true,
"neighbors": [
{
"ip": "10.0.0.2",
"remote_as": 65001,
"description": "IBGP_Peer_R2",
"next_hop_self": true,
"route_map_in": "INBOUND-FILTER"
},
{
"ip": "203.0.13.2",
"remote_as": 65002,
"description": "ISP_EBGP",
"ebgp_multihop": 2
}
],
"address_families": [
{
"type": "ipv4",
"networks": [
{
"address": "192.168.0.0",
"mask": "255.255.0.0"
}
],
"neighbors": [
{"ip": "10.0.0.2", "activate": true},
{"ip": "203.0.13.2", "activate": true}
]
}
]
}
}
```
**Rendered Configuration**:
```cisco
router bgp 65001
bgp router-id 1.1.1.1
bgp log-neighbor-changes
neighbor 10.0.0.2 remote-as 65001
neighbor 10.0.0.2 description IBGP_Peer_R2
neighbor 10.0.0.2 next-hop-self
neighbor 10.0.0.2 route-map INBOUND-FILTER in
neighbor 203.0.13.2 remote-as 65002
neighbor 203.0.13.2 description ISP_EBGP
neighbor 203.0.13.2 ebgp-multihop 2
address-family ipv4
network 192.168.0.0 mask 255.255.0.0
neighbor 10.0.0.2 activate
neighbor 203.0.13.2 activate
exit-address-family
!
```
---
### Example 3: Configure Interfaces with IP Addresses
**User Request**:
```
"Configure GigabitEthernet0/0 with IP 192.168.1.1/24, description 'LAN Network'.
Configure GigabitEthernet0/1 with IP 10.0.0.1/30, description 'WAN Link'.
Both interfaces should be enabled."
```
**AI Generates**:
```json
{
"interfaces": [
{
"name": "GigabitEthernet0/0",
"description": "LAN Network",
"ip_address": "192.168.1.1",
"subnet_mask": "255.255.255.0",
"enabled": true
},
{
"name": "GigabitEthernet0/1",
"description": "WAN Link",
"ip_address": "10.0.0.1",
"subnet_mask": "255.255.255.252",
"enabled": true
}
]
}
```
**Rendered Configuration**:
```cisco
interface GigabitEthernet0/0
description LAN Network
ip address 192.168.1.1 255.255.255.0
no shutdown
!
interface GigabitEthernet0/1
description WAN Link
ip address 10.0.0.1 255.255.255.252
no shutdown
!
```
---
## Advanced Examples
### Example 4: Multi-Feature Configuration
**User Request**:
```
"Configure R1 as follows:
- Hostname: CORE-R1
- GigabitEthernet0/0: 192.168.1.1/24, LAN, enable NAT inside
- GigabitEthernet0/1: 203.0.13.1/30, WAN, enable NAT outside
- OSPF: process 100, router-id 1.1.1.1, advertise 192.168.1.0/24 in area 0
- NAT: overload interface GigabitEthernet0/1 for 192.168.1.0/24"
```
**AI Generates** (complete configuration):
```json
{
"hostname": "CORE-R1",
"interfaces": [
{
"name": "GigabitEthernet0/0",
"description": "LAN",
"ip_address": "192.168.1.1",
"subnet_mask": "255.255.255.0",
"nat_inside": true,
"enabled": true
},
{
"name": "GigabitEthernet0/1",
"description": "WAN",
"ip_address": "203.0.13.1",
"subnet_mask": "255.255.255.252",
"nat_outside": true,
"enabled": true
}
],
"ospf": {
"enabled": true,
"process_id": 100,
"router_id": "1.1.1.1",
"networks": [
{
"address": "192.168.1.0",
"wildcard": "0.0.0.255",
"area": 0
}
]
},
"nat": {
"inside_source": {
"pool": "LAN_POOL",
"network": "192.168.1.0",
"mask": "255.255.255.0",
"interface": "GigabitEthernet0/1",
"overload": true
}
}
}
```
---
### Example 5: Juniper JunOS Configuration
**User Request**:
```
"Configure Juniper SRX with OSPF area 0 on interface ge-0/0/0.0 with IP 192.168.1.1/24.
Set router-id to 10.1.1.1."
```
**AI Generates**:
```json
{
"ospf": {
"enabled": true,
"router_id": "10.1.1.1",
"areas": [
{
"area_id": "0.0.0.0",
"interfaces": [
{
"name": "ge-0/0/0.0",
"address": "192.168.1.1/24"
}
]
}
]
}
}
```
**Template**: `config_templates/juniper/junos/ospf.j2`
```jinja2
{% if ospf.enabled %}
protocols {
ospf {
{% if ospf.router_id %}
router-id {{ ospf.router_id }};
{% endif %}
{% for area in ospf.areas %}
area {{ area.area_id }} {
{% for iface in area.interfaces %}
interface {{ iface.name }} {
{% if iface.address %}
family inet {
address {{ iface.address }};
}
{% endif %}
}
{% endfor %}
}
{% endfor %}
}
}
{% endif %}
```
**Rendered Configuration**:
```junos
protocols {
ospf {
router-id 10.1.1.1;
area 0.0.0.0 {
interface ge-0/0/0.0 {
family inet {
address 192.168.1.1/24;
}
}
}
}
}
```
---
## Integration with LangGraph Agent
### Updated Agent Flow
```python
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain.tools import tool
@tool
def render_and_apply_config(
node_id: str,
vendor: str,
os_type: str,
config_data: dict
) -> str:
"""Render configuration and apply to device"""
from gns3server.agent.gns3_copilot.config_renderer import ConfigRenderer
renderer = ConfigRenderer()
# Step 1: Validate
try:
renderer.validate_data(f"{vendor}_config", config_data)
except Exception as e:
return f"Validation failed: {e}"
# Step 2: Render
try:
if len(config_data) == 1:
feature = list(config_data.keys())[0]
config = renderer.render(vendor, os_type, feature, config_data)
else:
config = renderer.render_multi(vendor, os_type, config_data)
# Step 3: Apply to device (via telnet/console/SSH)
# result = apply_config_to_node(node_id, config)
return f"Configuration rendered successfully:\n{config}"
except Exception as e:
return f"Rendering failed: {e}"
# Updated agent prompt
SYSTEM_PROMPT = """
You are a network configuration assistant for GNS3.
When users ask to configure network devices:
1. Extract the configuration requirements
2. Generate STRUCTURED DATA (JSON/dict), NOT full configuration text
3. Call the render_and_apply_config tool with the structured data
4. The system will render the actual configuration using templates
Example for OSPF:
- User: "Configure OSPF with process 100, network 192.168.1.0/24 in area 0"
- You should output: {"ospf": {"enabled": true, "process_id": 100, ...}}
Available vendors: cisco, juniper, huawei, arista
Available OS types: ios, iosxr, nexus, junos, vrp, eos
"""
```
---
## Template Snippets Library
### OSPF Interface Templates
**Cisco IOS**:
```jinja2
{# ospf.j2 - Cisco IOS OSPF #}
{% if ospf.enabled %}
router ospf {{ ospf.process_id }}
{% if ospf.router_id %}
router-id {{ ospf.router_id }}
{% endif %}
{% for network in ospf.networks %}
network {{ network.address }} mask {{ network.wildcard }} area {{ network.area }}
{% endfor %}
{% for iface in ospf.passive_interfaces %}
passive-interface {{ iface }}
{% endfor %}
!
{% endif %}
```
**Juniper JunOS**:
```jinja2
{# ospf.j2 - Juniper JunOS OSPF #}
{% if ospf.enabled %}
protocols {
ospf {
{% if ospf.router_id %}
router-id {{ ospf.router_id }};
{% endif %}
{% for area in ospf.areas %}
area {{ area.area_id }} {
{% for iface in area.interfaces %}
interface {{ iface.name }};
{% endfor %}
}
{% endfor %}
}
}
{% endif %}
```
**Huawei VRP**:
```jinja2
{# ospf.j2 - Huawei VRP OSPF #}
{% if ospf.enabled %}
ospf {{ ospf.process_id }}
{% if ospf.router_id %}
router-id {{ ospf.router_id }}
{% endif %}
{% for area in ospf.areas %}
area {{ area.area_id }}
{% for network in area.networks %}
network {{ network.address }} {{ network.wildcard }}
{% endfor %}
{% endfor %}
{% endif %}
```
---
## Testing Framework
### Unit Test for Template Rendering
```python
# tests/agent/test_config_renderer.py
import pytest
from gns3server.agent.gns3_copilot.config_renderer import ConfigRenderer
def test_ospf_cisco_ios():
"""Test OSPF configuration rendering for Cisco IOS"""
renderer = ConfigRenderer()
data = {
"ospf": {
"enabled": True,
"process_id": 100,
"router_id": "1.1.1.1",
"networks": [
{"address": "192.168.1.0", "wildcard": "0.0.0.255", "area": 0}
]
}
}
config = renderer.render("cisco", "ios", "ospf", data)
assert "router ospf 100" in config
assert "router-id 1.1.1.1" in config
assert "network 192.168.1.0 mask 0.0.0.255 area 0" in config
def test_bgp_cisco_ios():
"""Test BGP configuration rendering for Cisco IOS"""
renderer = ConfigRenderer()
data = {
"bgp": {
"enabled": True,
"as_number": 65001,
"neighbors": [
{"ip": "10.0.0.2", "remote_as": 65002}
],
"address_families": [
{
"type": "ipv4",
"neighbors": [
{"ip": "10.0.0.2", "activate": True}
]
}
]
}
}
config = renderer.render("cisco", "ios", "bgp", data)
assert "router bgp 65001" in config
assert "neighbor 10.0.0.2 remote-as 65002" in config
assert "address-family ipv4" in config
assert "neighbor 10.0.0.2 activate" in config
def test_interface_cisco_ios():
"""Test interface configuration rendering"""
renderer = ConfigRenderer()
data = {
"interfaces": [
{
"name": "GigabitEthernet0/0",
"description": "Test Interface",
"ip_address": "192.168.1.1",
"subnet_mask": "255.255.255.0",
"enabled": True
}
]
}
config = renderer.render("cisco", "ios", "interface", data)
assert "interface GigabitEthernet0/0" in config
assert "description Test Interface" in config
assert "ip address 192.168.1.1 255.255.255.0" in config
assert "no shutdown" in config
```
---
## API Integration
### New Controller Endpoint
```python
# gns3server/api/routes/controller/config_templates.py
from fastapi import APIRouter, Depends
from typing import Dict, Any
from gns3server.agent.gns3_copilot.config_renderer import ConfigRenderer
router = APIRouter()
@router.get("/config-templates")
async def list_templates() -> Dict[str, Any]:
"""List all available configuration templates"""
renderer = ConfigRenderer()
return renderer.get_available_templates()
@router.post("/config-templates/render")
async def render_config_template(
vendor: str,
os_type: str,
feature: str,
data: Dict[str, Any]
) -> Dict[str, str]:
"""Render a configuration template with provided data"""
renderer = ConfigRenderer()
try:
config = renderer.render(vendor, os_type, feature, data)
return {"status": "success", "config": config}
except Exception as e:
return {"status": "error", "message": str(e)}
@router.post("/config-templates/validate")
async def validate_config_data(
schema_name: str,
data: Dict[str, Any]
) -> Dict[str, Any]:
"""Validate configuration data against schema"""
renderer = ConfigRenderer()
try:
is_valid = renderer.validate_data(schema_name, data)
return {"status": "valid"}
except Exception as e:
return {"status": "invalid", "errors": str(e)}
```
---
## Prompt Engineering for AI
### System Prompt Template
```python
CONFIG_GENERATION_PROMPT = """
You are a network configuration expert. When users request device configurations:
1. UNDERSTAND the requirements (vendor, OS, features, parameters)
2. GENERATE structured data (dict/JSON), NOT full configuration text
3. CALL the appropriate rendering tool with the structured data
RULES:
- NEVER output full configuration text directly
- ALWAYS use structured data format
- Include only the parameters that are explicitly mentioned
- Use correct data types (int for numbers, bool for flags)
- Follow the JSON schema for each feature
VENDORS: cisco, juniper, huawei, arista, mikrotik
OS TYPES: ios, iosxr, nx-os, junos, vrp, eos, routeros
FEATURES AVAILABLE:
- ospf: process_id, router_id, networks[{address,wildcard,area}]
- bgp: as_number, router_id, neighbors[{ip,remote_as,description,...}]
- interface: name, ip_address, subnet_mask, description, enabled
- vlan: id, name, interfaces[]
- acl: number, rules[{action,protocol,source,destination}]
- nat: inside_source, outside_source, static
EXAMPLE:
User: "Configure OSPF process 100 with router-id 1.1.1.1, network 192.168.1.0/24 area 0"
Your tool call:
render_device_config(
node_id="node-1",
vendor="cisco",
os_type="ios",
config_data={{
"ospf": {{
"enabled": True,
"process_id": 100,
"router_id": "1.1.1.1",
"networks": [
{{"address": "192.168.1.0", "wildcard": "0.0.0.255", "area": 0}}
]
}}
}}
)
"""
```
---
## Migration Path
### Phase 1: Core Templates (Week 1-2)
- Cisco IOS: ospf, bgp, interface, vlan, acl
- Juniper JunOS: ospf, bgp, interface
- Schema definitions
### Phase 2: Extended Features (Week 3-4)
- NAT, QoS, Multicast
- Nexus, IOS-XR variants
- Huawei VRP support
### Phase 3: Advanced Features (Week 5-6)
- MPLS, VPN
- Firewall policies (ASA, SRX)
- Automation and testing
### Phase 4: Integration (Week 7-8)
- Integrate with AI Copilot
- Add rendering endpoint to API
- Testing and validation
---
## Best Practices
1. **Template Design**:
- Keep templates simple and focused
- Use conditionals sparingly
- Add comments for complex logic
- Follow vendor syntax conventions
2. **Schema Design**:
- Define all fields with types
- Add descriptions for AI
- Include validation rules
- Use enums for fixed values
3. **AI Prompting**:
- Provide clear examples
- Specify expected output format
- Include error handling guidance
- Test with various inputs
4. **Testing**:
- Unit test each template
- Test with real devices
- Validate schemas
- Integration testing
---
## Troubleshooting
### Common Issues
**Issue**: Template not found
```
Solution: Check template path format: "{vendor}/{os_type}/{feature}.j2"
```
**Issue**: Invalid data structure
```
Solution: Validate against JSON schema first
```
**Issue**: Rendering produces empty config
```
Solution: Check if feature flag "enabled" is set to True
```
**Issue**: Syntax error in rendered config
```
Solution: Review template logic, check conditional statements
```
---
## License
**Copyright © 2025 Yue Guobin (岳国宾)**
This work is licensed under the [Creative Commons Attribution-ShareAlike 4.0
International License (CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/).
![CC BY-SA 4.0](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)
### Summary
You are free to:
- **Share** — Copy and redistribute the material in any medium or format
- **Adapt** — Remix, transform, and build upon the material for any purpose
Under the following terms:
- **Attribution** — You must give appropriate credit to **Yue Guobin (岳国宾)**, provide
a link to the license, and indicate if changes were made.
- **ShareAlike** — If you remix, transform, or build upon the material, you must
distribute your contributions under the **same license** (CC BY-SA 4.0).
Full license text: [DESIGN_DOCS_LICENSE](../DESIGN_DOCS_LICENSE.md)