Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| MCP Tool: Auto-configure GreenLeaf Coffee network switches (HQ + dynamic stores) | |
| Uses telnet to send initial configuration to all switches | |
| """ | |
| import telnetlib | |
| import time | |
| import requests | |
| import re | |
| import os | |
| GNS3_SERVER = os.getenv("GNS3_SERVER", "http://localhost:3080") | |
| GNS3_API = f"{GNS3_SERVER}/v2" | |
| PROJECT_NAME = os.getenv("GNS3_PROJECT_NAME", "overgrowth") | |
| def get_actual_console_ports(): | |
| """Extract real console ports from QEMU processes""" | |
| import subprocess | |
| result = subprocess.run(['ps', 'aux'], capture_output=True, text=True) | |
| ports = {} | |
| for line in result.stdout.split('\n'): | |
| if 'qemu-system-x86_64' not in line: | |
| continue | |
| name = None | |
| if 'SW-HQ-Core' in line: | |
| name = 'SW-HQ-Core' | |
| else: | |
| m = re.search(r'SW-Store-(\d+)', line) | |
| if m: | |
| name = f"SW-Store-{m.group(1)}" | |
| if not name: | |
| continue | |
| match = re.search(r'-serial telnet:127\.0\.0\.1:(\d+)', line) | |
| if match: | |
| ports[name] = int(match.group(1)) | |
| return ports | |
| def configure_switch(host, port, switch_name, config_commands): | |
| """Send configuration to switch via telnet""" | |
| print(f"\n{'='*60}") | |
| print(f"Configuring {switch_name} on port {port}...") | |
| print('='*60) | |
| try: | |
| tn = telnetlib.Telnet(host, port, timeout=10) | |
| # Wake up console | |
| tn.write(b"\r\n") | |
| time.sleep(1) | |
| # Read initial output | |
| output = tn.read_very_eager().decode('utf-8', errors='ignore') | |
| # Check if we need to press RETURN to start | |
| if "Press RETURN to get started" in output or output.strip() == "": | |
| print(" → Pressing RETURN to start...") | |
| tn.write(b"\r\n") | |
| time.sleep(2) | |
| output = tn.read_very_eager().decode('utf-8', errors='ignore') | |
| # Enter enable mode if at user prompt | |
| if ">" in output: | |
| print(" → Entering enable mode...") | |
| tn.write(b"enable\r\n") | |
| time.sleep(1) | |
| tn.read_very_eager() | |
| # Send each config command | |
| for cmd in config_commands: | |
| print(f" → {cmd}") | |
| tn.write(cmd.encode('utf-8') + b"\r\n") | |
| time.sleep(0.5) | |
| response = tn.read_very_eager().decode('utf-8', errors='ignore') | |
| # Check for errors | |
| if "Invalid" in response or "Error" in response: | |
| print(f" ⚠️ Warning: {response[:100]}") | |
| # Exit config mode | |
| tn.write(b"end\r\n") | |
| time.sleep(0.5) | |
| # Save config | |
| print(" → Saving configuration...") | |
| tn.write(b"write memory\r\n") | |
| time.sleep(2) | |
| response = tn.read_very_eager().decode('utf-8', errors='ignore') | |
| if "OK" in response or "[OK]" in response: | |
| print(" ✅ Configuration saved successfully!") | |
| tn.close() | |
| return True | |
| except Exception as e: | |
| print(f" ❌ Error: {e}") | |
| return False | |
| def main(): | |
| print("="*70) | |
| print("🌿 GREENLEAF COFFEE - AUTOMATED NETWORK CONFIGURATION") | |
| print("="*70) | |
| print("\nUsing MCP automation to configure all switches...\n") | |
| # Get actual console ports | |
| print("🔍 Discovering switch console ports...") | |
| ports = get_actual_console_ports() | |
| if not ports: | |
| print("❌ No switches found! Make sure they're running.") | |
| return | |
| print(f"✅ Found {len(ports)} switches:") | |
| for name, port in ports.items(): | |
| print(f" • {name:20} → telnet://localhost:{port}") | |
| def hq_config(): | |
| return [ | |
| "configure terminal", | |
| "hostname SW-HQ-Core", | |
| "!", | |
| "! VLAN Configuration", | |
| "vlan 10", | |
| " name Sales", | |
| "vlan 20", | |
| " name Engineering", | |
| "vlan 99", | |
| " name Management", | |
| "exit", | |
| "!", | |
| "! Management interface", | |
| "interface vlan 99", | |
| " ip address 10.99.0.1 255.255.255.0", | |
| " no shutdown", | |
| "exit", | |
| "!", | |
| "! WAN uplink", | |
| "interface ethernet 15", | |
| " description WAN to Internet/MPLS", | |
| " switchport trunk encapsulation dot1q", | |
| " switchport mode trunk", | |
| " no shutdown", | |
| "!", | |
| "! Server port", | |
| "interface ethernet 0", | |
| " description Server-DB", | |
| " switchport mode access", | |
| " switchport access vlan 99", | |
| " spanning-tree portfast", | |
| " no shutdown", | |
| "!", | |
| "! Office PC port", | |
| "interface ethernet 1", | |
| " description Office-Manager", | |
| " switchport mode access", | |
| " switchport access vlan 99", | |
| " spanning-tree portfast", | |
| " no shutdown", | |
| "!", | |
| "! Enable SSH", | |
| "ip domain-name greenleaf.local", | |
| "crypto key generate rsa modulus 2048", | |
| "!", | |
| "username admin privilege 15 secret overgrowth123", | |
| "line vty 0 4", | |
| " login local", | |
| " transport input ssh", | |
| "!", | |
| "! Banner", | |
| "banner motd # GreenLeaf Coffee HQ - Authorized Access Only #", | |
| ] | |
| def store_switch_config(name: str, idx: int) -> list: | |
| mgmt_octet = 10 + idx if idx > 0 else 10 | |
| return [ | |
| "configure terminal", | |
| f"hostname {name}", | |
| "!", | |
| "! VLAN Configuration", | |
| "vlan 10", | |
| " name Sales", | |
| "vlan 99", | |
| " name Management", | |
| "exit", | |
| "!", | |
| "! Management interface", | |
| "interface vlan 99", | |
| f" ip address 10.99.0.{mgmt_octet} 255.255.255.0", | |
| " no shutdown", | |
| "exit", | |
| "!", | |
| "! Trunk to WAN", | |
| "interface ethernet 15", | |
| " description WAN to Internet/MPLS", | |
| " switchport trunk encapsulation dot1q", | |
| " switchport mode trunk", | |
| " no shutdown", | |
| "!", | |
| "! POS Terminal", | |
| "interface ethernet 0", | |
| f" description POS-{name}", | |
| " switchport mode access", | |
| " switchport access vlan 10", | |
| " spanning-tree portfast", | |
| " no shutdown", | |
| "!", | |
| "! WiFi Access Point", | |
| "interface ethernet 1", | |
| f" description WiFi-{name}", | |
| " switchport mode access", | |
| " switchport access vlan 10", | |
| " spanning-tree portfast", | |
| " no shutdown", | |
| "!", | |
| "! Enable SSH", | |
| "ip domain-name greenleaf.local", | |
| "crypto key generate rsa modulus 2048", | |
| "username admin privilege 15 secret overgrowth123", | |
| "line vty 0 4", | |
| " login local", | |
| " transport input ssh", | |
| "!", | |
| f"banner motd # GreenLeaf Coffee {name} - Authorized Access Only #", | |
| ] | |
| # Configure each switch | |
| results = {} | |
| for switch_name, port in ports.items(): | |
| if switch_name == 'SW-HQ-Core': | |
| commands = hq_config() | |
| elif switch_name.startswith('SW-Store-'): | |
| idx_match = re.search(r'SW-Store-(\d+)', switch_name) | |
| idx = int(idx_match.group(1)) if idx_match else 0 | |
| commands = store_switch_config(switch_name, idx) | |
| else: | |
| continue | |
| success = configure_switch('localhost', port, switch_name, commands) | |
| results[switch_name] = success | |
| # Summary | |
| print("\n" + "="*70) | |
| print("CONFIGURATION SUMMARY") | |
| print("="*70) | |
| for switch, success in results.items(): | |
| status = "✅ SUCCESS" if success else "❌ FAILED" | |
| print(f" {switch:20} {status}") | |
| if results: | |
| if all(results.values()): | |
| print("\n🎉 ALL SWITCHES CONFIGURED!") | |
| else: | |
| print("\n⚠️ Some switches failed to configure") | |
| print(" Check console output for errors") | |
| else: | |
| print("\n⚠️ No switches were configured") | |
| if results: | |
| print("\n📋 Network Details:") | |
| print(" • HQ Management IP: 10.99.0.1") | |
| for name in sorted(results.keys()): | |
| if name.startswith("SW-Store-"): | |
| m = re.search(r"SW-Store-(\d+)", name) | |
| idx = int(m.group(1)) if m else 0 | |
| mgmt_ip = f"10.99.0.{10 + idx if idx > 0 else 10}" | |
| print(f" • {name:18} Mgmt IP: {mgmt_ip}") | |
| print("\n🔐 SSH Credentials:") | |
| print(" • Username: admin") | |
| print(" • Password: overgrowth123") | |
| print("\n✨ Next: SSH into switches for remote management!") | |
| print(" ssh [email protected]") | |
| print("="*70) | |
| if __name__ == "__main__": | |
| main() | |