# Stage 6: Autonomous Deployment Guide Complete guide for deploying configurations to real network devices using Overgrowth's autonomous deployment engine. ## Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ Deployment Orchestration │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ 1. Config Generation (Jinja2 Templates) │ │ │ │ 2. Pre-Deployment Validation │ │ │ │ 3. Device Connection (Netmiko/NAPALM) │ │ │ │ 4. Configuration Deployment │ │ │ │ 5. Post-Deployment Verification │ │ │ │ 6. Automatic Rollback (on failure) │ │ │ └────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Cisco │ │ Arista │ │ Juniper │ │ IOS/NXOS │ │ EOS │ │ JunOS │ └──────────┘ └──────────┘ └──────────┘ ``` ## Components ### 1. DeviceDriver (`agent/device_driver.py`) Manages connections and config deployment to network devices. **Supported Platforms:** - Cisco IOS - Cisco NXOS (Nexus) - Cisco IOS-XE (ASR, ISR, etc.) - Arista EOS - Juniper JunOS **Features:** - Connection pooling and management - Config backup before deployment - Automatic rollback on failure - Dry-run mode (validate without deploying) - Mock mode (testing without devices) ### 2. ConfigTemplateEngine (`agent/config_templates.py`) Generates device configs from Jinja2 templates. **Built-in Templates:** - `cisco_ios_l2_switch` - Layer 2 access switch - `cisco_ios_l3_router` - Layer 3 router with OSPF/BGP - `arista_eos` - Arista EOS switch/router - `juniper_junos` - Juniper router/switch **Features:** - Variable substitution from NetworkModel - Custom template support - Template validation - Vendor-specific config generation ### 3. DeploymentEngine (`agent/deployment_engine.py`) Orchestrates the entire deployment workflow. **Workflow:** 1. Generate config from template 2. Connect to device 3. Run pre-deployment checks 4. Backup current config 5. Deploy new config 6. Run post-deployment checks 7. Rollback if checks fail 8. Record deployment history ## Usage Examples ### Example 1: Deploy Single Device ```python from agent.deployment_engine import DeploymentEngine, DeploymentTask, DeviceType # Initialize engine deployer = DeploymentEngine(use_napalm=True) # Create deployment task task = DeploymentTask( device_id='core-sw-1', device_type=DeviceType.CISCO_IOS, hostname='192.168.1.10', username='admin', password='admin123', config=""" hostname core-sw-1 ! vlan 10 name DATA vlan 20 name VOICE ! interface GigabitEthernet0/1 switchport mode access switchport access vlan 10 no shutdown ! """, dry_run=False, pre_checks=['command:show version'], post_checks=['interface:GigabitEthernet0/1'] ) # Deploy result = deployer.deploy_single_device(task) print(f"Status: {result.status.value}") print(f"Duration: {result.duration_seconds:.1f}s") if result.status.value == 'success': print("✓ Deployment successful!") else: print(f"✗ Deployment failed: {result.error}") if result.rolled_back: print("✓ Configuration rolled back") ``` ### Example 2: Generate and Deploy from Template ```python from agent.deployment_engine import DeploymentEngine from agent.pipeline_engine import NetworkModel, Device, NetworkIntent # Create network model model = NetworkModel( name="campus-network", version="1.0", intent=NetworkIntent( description="Campus network deployment", business_requirements=["High availability", "VLAN segmentation"], constraints=["Budget friendly"] ), devices=[ Device( name="access-sw-1", role="access", model="Catalyst 2960", vendor="Cisco", mgmt_ip="192.168.1.20", location="Building A", interfaces=[ { "name": "GigabitEthernet0/1", "description": "Uplink to core", "mode": "trunk", "enabled": True }, { "name": "GigabitEthernet0/2", "description": "Workstation port", "mode": "access", "vlan": 10, "enabled": True } ] ) ], vlans=[ {"id": 10, "name": "DATA"}, {"id": 20, "name": "VOICE"}, {"id": 99, "name": "MANAGEMENT"} ], subnets=[ {"network": "10.0.10.0/24", "vlan": 10}, {"network": "10.0.20.0/24", "vlan": 20} ], routing={}, services=["DHCP", "NTP"] ) # Initialize deployer deployer = DeploymentEngine(use_napalm=True) # Network context for templates network_context = { 'vlans': model.vlans, 'routing': model.routing, 'domain_name': 'campus.local', 'ntp_servers': ['0.pool.ntp.org'], 'dns_servers': ['8.8.8.8'] } # Credentials credentials = { 'username': 'admin', 'password': 'secure123' } # Deploy each device for device in model.devices: result = deployer.generate_and_deploy( device=device, network_context=network_context, credentials=credentials, dry_run=False, pre_checks=['command:show version'], post_checks=['command:show running-config'] ) print(f"{device.name}: {result.status.value}") ``` ### Example 3: Dry-Run Mode (Test Without Deploying) ```python from agent.pipeline_engine import OvergrowthPipeline pipeline = OvergrowthPipeline() # Generate network model intent = pipeline.stage1_consultation("Deploy 3-tier campus network") model = pipeline.stage2_generate_sot(intent) # Dry-run deployment (generates configs, validates, but doesn't deploy) results = pipeline.stage6_autonomous_deploy( model=model, credentials={'username': 'admin', 'password': 'admin'}, dry_run=True, # No actual changes to devices parallel=False ) print(f"Dry-run complete: {results['successful']}/{results['total_devices']} would succeed") for r in results['results']: print(f" {r['device_id']}: {r['status']}") if r['status'] == 'failed': print(f" Error: {r['error']}") ``` ### Example 4: Parallel Deployment with Ray ```python from agent.pipeline_engine import OvergrowthPipeline pipeline = OvergrowthPipeline() model = pipeline.stage2_generate_sot(intent) # Enable parallel mode pipeline.enable_parallel_mode() # Deploy to all devices in parallel results = pipeline.stage6_autonomous_deploy( model=model, credentials={'username': 'admin', 'password': 'admin'}, dry_run=False, parallel=True # Use Ray for concurrent deployment ) print(f"Deployed to {results['successful']}/{results['total_devices']} devices") print(f"Success rate: {results['success_rate']:.1f}%") print(f"Rolled back: {results['rolled_back']}") ``` ### Example 5: Custom Pre/Post Validation Checks ```python from agent.deployment_engine import DeploymentEngine, DeploymentTask, DeviceType deployer = DeploymentEngine() task = DeploymentTask( device_id='border-rtr-1', device_type=DeviceType.CISCO_XE, hostname='10.0.0.1', username='admin', password='admin', config=router_config, dry_run=False, # Pre-deployment checks pre_checks=[ 'command:show version', 'command:show ip interface brief', 'ping:8.8.8.8', # Check internet connectivity ], # Post-deployment checks post_checks=[ 'interface:GigabitEthernet0/0', # Verify interface up 'ping:10.0.1.1', # Verify internal connectivity 'command:show ip bgp summary', # Verify BGP ] ) result = deployer.deploy_single_device(task) # Check which validations passed/failed print("Pre-checks:", result.pre_check_results) print("Post-checks:", result.post_check_results) ``` ## Configuration Templates ### Cisco IOS L2 Switch Template Located in `config_templates.py` as `CISCO_IOS_L2_SWITCH_TEMPLATE`. **Variables:** - `device.name` - Hostname - `device.mgmt_ip` - Management IP - `vlans` - List of VLAN dicts (`id`, `name`) - `device.interfaces` - List of interface dicts - `default_gateway` - Default gateway IP - `ntp_servers` - List of NTP server IPs - `dns_servers` - List of DNS server IPs **Example:** ```python from agent.config_templates import generate_cisco_ios_config config = generate_cisco_ios_config( device=my_device, vlans=[ {"id": 10, "name": "DATA"}, {"id": 20, "name": "VOICE"} ], ntp_servers=['0.pool.ntp.org'], dns_servers=['8.8.8.8'], default_gateway='192.168.1.1' ) ``` ### Cisco IOS L3 Router Template Includes routing protocols (OSPF, BGP, static routes). **Variables:** - All L2 variables plus: - `routing.protocol` - `'ospf'`, `'bgp'`, or `'static'` - `routing.process_id` - OSPF process ID - `routing.networks` - List of networks to advertise - `routing.asn` - BGP AS number - `routing.neighbors` - List of BGP neighbor dicts **Example:** ```python config = generate_cisco_ios_config( device=router_device, vlans=[], routing={ 'protocol': 'ospf', 'process_id': 1, 'networks': ['10.0.0.0 0.0.255.255'], 'area': 0 } ) ``` ### Custom Templates Create custom Jinja2 templates: ```python from agent.config_templates import ConfigTemplateEngine engine = ConfigTemplateEngine() # Add custom template custom_template = """ hostname {{ device.name }} ! {% for vlan in vlans %} vlan {{ vlan.id }} name {{ vlan.name }} {% endfor %} ! """ engine.add_custom_template('my_custom_template', custom_template) # Use it config = engine.render_template('my_custom_template', { 'device': {'name': 'my-switch'}, 'vlans': [{'id': 10, 'name': 'DATA'}] }) ``` ## Validation Checks ### Check Types **Command checks:** ```python 'command:show version' # Run command, pass if no error ``` **Ping checks:** ```python 'ping:8.8.8.8' # Ping target, pass if successful ``` **Interface checks:** ```python 'interface:GigabitEthernet0/1' # Check interface status, pass if up ``` ### Check Timing - **Pre-checks:** Run before config deployment - Verify device accessible - Check current state - Validate prerequisites - **Post-checks:** Run after config deployment - Verify config applied - Test connectivity - Validate services ## Error Handling & Rollback ### Automatic Rollback If post-deployment checks fail, the engine automatically rolls back: 1. Detect check failure 2. Log error details 3. Deploy previous config (from backup) 4. Mark deployment as `ROLLED_BACK` ```python result = deployer.deploy_single_device(task) if result.rolled_back: print(f"Deployment failed and was rolled back") print(f"Reason: {result.error}") print(f"Config restored to: {result.config_before[:100]}...") ``` ### Manual Rollback ```python from agent.device_driver import DeviceDriver, DeviceCredentials, DeviceType driver = DeviceDriver() # Connect creds = DeviceCredentials( hostname='192.168.1.10', username='admin', password='admin', device_type=DeviceType.CISCO_IOS ) conn = driver.connect(creds) # Get current config backup = driver.get_config('192.168.1.10', 'running') # ... something goes wrong ... # Rollback driver.rollback_config('192.168.1.10', backup) ``` ## Multi-Vendor Support ### Cisco IOS/IOS-XE ```python from agent.device_driver import DeviceType # Cisco Catalyst, ISR, ASR device_type = DeviceType.CISCO_IOS # or CISCO_XE ``` **Supported Features:** - Config merge and replace - Running/startup config backup - Auto-save on Cisco IOS ### Cisco NXOS (Nexus) ```python device_type = DeviceType.CISCO_NXOS ``` **Features:** - Checkpoint/rollback support - Config replace via NAPALM ### Arista EOS ```python device_type = DeviceType.ARISTA_EOS ``` **Features:** - Config sessions - Atomic commits - Fast boot times ### Juniper JunOS ```python device_type = DeviceType.JUNIPER_JUNOS ``` **Features:** - Candidate config - Commit confirmed - Rollback points ## Testing Without Devices (Mock Mode) All components support mock mode for development/testing: ```python from agent.device_driver import DeviceDriver # Initialize in mock mode (auto-detected if Netmiko/NAPALM not installed) driver = DeviceDriver() print(f"Mock mode: {driver.mock_mode}") # True if no libraries # Mock connections always succeed conn = driver.connect(credentials) print(f"Connected: {conn.status}") # CONNECTED # Mock deployments simulate success result = driver.deploy_config('device-1', config) print(f"Deployed: {result.success}") # True ``` ## Deployment History & Metrics ```python from agent.deployment_engine import DeploymentEngine deployer = DeploymentEngine() # ... deploy devices ... # Get summary summary = deployer.get_deployment_summary() print(f"Total deployments: {summary['total_deployments']}") print(f"Success rate: {summary['success_rate']:.1f}%") print(f"Average duration: {summary['avg_duration']:.1f}s") # Recent deployments for dep in summary['latest_deployments']: print(f"{dep['device_id']}: {dep['status']} ({dep['duration']:.1f}s)") ``` ## Troubleshooting ### Connection Failures **Symptom:** `Failed to connect: timeout` **Solutions:** ```python # Increase timeout credentials = DeviceCredentials( hostname='192.168.1.10', username='admin', password='admin', device_type=DeviceType.CISCO_IOS, timeout=60 # Increase from default 30s ) # Check network connectivity driver.verify_connectivity('device-id') # Enable session logging for debugging credentials.session_log = '/tmp/device-session.log' ``` ### Authentication Failures **Symptom:** `Failed to connect: authentication failed` **Solutions:** ```python # For devices requiring enable password credentials = DeviceCredentials( hostname='192.168.1.10', username='admin', password='admin', secret='enable_password', # Enable secret device_type=DeviceType.CISCO_IOS ) ``` ### Config Deployment Failures **Symptom:** `Deployment failed: command error` **Solutions:** ```python # Use dry-run to validate first result = deployer.deploy_config( device_id='device-1', config=config, dry_run=True # Test without applying ) print(f"Would work: {result.success}") # Check diff before deploying if result.output: print(f"Changes:\n{result.output}") ``` ### Post-Check Failures **Symptom:** `Post-deployment check failed: ping:8.8.8.8` **Solutions:** ```python # Add delay before post-checks import time time.sleep(5) # Wait for config to take effect # Use more specific checks post_checks=[ 'command:show ip interface brief', # More specific than ping 'interface:GigabitEthernet0/1' ] # Disable rollback for troubleshooting # (manually verify and fix) ``` ## Production Best Practices ### 1. Always Use Dry-Run First ```python # Test deployment dry_result = pipeline.stage6_autonomous_deploy(model, dry_run=True) # Review results if dry_result['success_rate'] == 100.0: # Now deploy for real real_result = pipeline.stage6_autonomous_deploy(model, dry_run=False) ``` ### 2. Use Pre-Flight Validation ```python # Run Stage 0 validation before deployment preflight = pipeline.stage0_preflight(model) if not preflight['ready_to_deploy']: print("Pre-flight failed - aborting") print(f"Errors: {preflight['errors']}") exit(1) # Deploy only after validation passes pipeline.stage6_autonomous_deploy(model) ``` ### 3. Implement Change Windows ```python from datetime import datetime, time as dt_time def in_change_window(): """Check if current time is in approved change window""" now = datetime.now() # Only deploy between 2 AM - 4 AM return dt_time(2, 0) <= now.time() <= dt_time(4, 0) if not in_change_window(): print("Outside change window - aborting") exit(1) # Deploy during approved window pipeline.stage6_autonomous_deploy(model) ``` ### 4. Use Parallel Deployment Carefully ```python # Start with small batch results = pipeline.parallel_deploy_fleet( model=model, staggered=True, stages=[0.01, 0.05, 0.1, 1.0] # 1%, 5%, 10%, 100% ) # Circuit breaker stops on high failure rate ``` ### 5. Maintain Deployment Audit Trail ```python deployer = DeploymentEngine() # Deploy result = deployer.deploy_single_device(task) # Log to external system import json with open(f'/var/log/deployments/{result.device_id}.json', 'w') as f: json.dump({ 'device_id': result.device_id, 'status': result.status.value, 'timestamp': result.timestamp.isoformat(), 'config_before': result.config_before, 'config_after': result.config_after, 'deployed_by': os.environ.get('USER'), 'duration': result.duration_seconds }, f, indent=2) ``` ## Next Steps - ✅ Multi-vendor device support - ✅ Config templating - ✅ Pre/post validation - ✅ Automatic rollback - 🚧 Full Ray parallel deployment integration - 🚧 Advanced validation (pyATS test cases) - 🚧 Change request workflow - 🚧 Approval gates for production **You're now ready to deploy to real network devices!** 🚀