Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Rebuild CCNA topology with: | |
| - Proper switch icons | |
| - Single trunk links (no redundancy) | |
| - Clean hierarchical layout | |
| - Simple access-core-access design | |
| """ | |
| import requests | |
| import json | |
| 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") | |
| IOSVL2_TEMPLATE_ID = "25dd7340-2e92-4e45-83de-f88077a24ceb" | |
| def get_project_id(name): | |
| resp = requests.get(f"{GNS3_API}/projects") | |
| projects = resp.json() | |
| project = next((p for p in projects if p['name'] == name), None) | |
| return project['project_id'] if project else None | |
| def delete_all_nodes(project_id): | |
| resp = requests.get(f"{GNS3_API}/projects/{project_id}/nodes") | |
| nodes = resp.json() | |
| print(f"ποΈ Cleaning up {len(nodes)} existing nodes...") | |
| for node in nodes: | |
| requests.delete(f"{GNS3_API}/projects/{project_id}/nodes/{node['node_id']}") | |
| def create_switch(project_id, name, x, y): | |
| """Create Cisco IOSvL2 switch with PROPER ICON""" | |
| data = { | |
| "name": name, | |
| "node_type": "qemu", | |
| "compute_id": "local", | |
| "template_id": IOSVL2_TEMPLATE_ID, | |
| "properties": { | |
| "qemu_path": "/usr/bin/qemu-system-x86_64", | |
| "platform": "x86_64", | |
| "adapters": 16, | |
| "ram": 1024, | |
| "hda_disk_image": "vios_l2-adventerprisek9-m.03.2017.qcow2" | |
| }, | |
| "symbol": ":/symbols/multilayer_switch.svg", # PROPER SWITCH ICON! | |
| "x": x, | |
| "y": y | |
| } | |
| resp = requests.post(f"{GNS3_API}/projects/{project_id}/nodes", json=data) | |
| return resp.json() if resp.status_code in [200, 201] else None | |
| def create_pc(project_id, name, x, y): | |
| """Create VPCS with proper computer icon""" | |
| data = { | |
| "name": name, | |
| "node_type": "vpcs", | |
| "compute_id": "local", | |
| "symbol": ":/symbols/vpcs_guest.svg", # Better PC icon | |
| "x": x, | |
| "y": y | |
| } | |
| resp = requests.post(f"{GNS3_API}/projects/{project_id}/nodes", json=data) | |
| return resp.json() if resp.status_code in [200, 201] else None | |
| def create_link(project_id, node1_id, adapter1, port1, node2_id, adapter2, port2, desc=""): | |
| data = { | |
| "nodes": [ | |
| {"node_id": node1_id, "adapter_number": adapter1, "port_number": port1}, | |
| {"node_id": node2_id, "adapter_number": adapter2, "port_number": port2} | |
| ] | |
| } | |
| resp = requests.post(f"{GNS3_API}/projects/{project_id}/links", json=data) | |
| if resp.status_code in [200, 201]: | |
| print(f" β {desc}") | |
| return True | |
| else: | |
| print(f" β {desc} - {resp.status_code}") | |
| return False | |
| def main(): | |
| project_id = get_project_id(PROJECT_NAME) | |
| if not project_id: | |
| print(f"Project '{PROJECT_NAME}' not found!") | |
| return | |
| print("="*70) | |
| print("REBUILDING CLEAN CCNA TOPOLOGY") | |
| print("="*70) | |
| print() | |
| delete_all_nodes(project_id) | |
| print("π Creating topology with hierarchical layout...\n") | |
| # Clean hierarchical layout: | |
| # Top row: SW2-Core (center, top) | |
| # Middle row: SW1-Access (left) and SW3-Access (right) | |
| # Bottom rows: PCs under each switch | |
| nodes = {} | |
| # CORE SWITCH (top center) | |
| print("Creating Core Switch...") | |
| nodes['SW2'] = create_switch(project_id, "SW2-Core", 0, -150) | |
| if nodes['SW2']: print(" β SW2-Core (Distribution/Core)") | |
| # ACCESS SWITCHES (middle tier) | |
| print("\nCreating Access Switches...") | |
| nodes['SW1'] = create_switch(project_id, "SW1-Sales", -350, 0) | |
| if nodes['SW1']: print(" β SW1-Sales (Access, VLAN 10)") | |
| nodes['SW3'] = create_switch(project_id, "SW3-Eng", 350, 0) | |
| if nodes['SW3']: print(" β SW3-Eng (Access, VLAN 20)") | |
| # PCs (bottom tier, under their access switches) | |
| print("\nCreating Virtual PCs...") | |
| nodes['PC1'] = create_pc(project_id, "PC1-Sales", -450, 150) | |
| if nodes['PC1']: print(" β PC1-Sales (10.1.10.10)") | |
| nodes['PC2'] = create_pc(project_id, "PC2-Sales", -250, 150) | |
| if nodes['PC2']: print(" β PC2-Sales (10.1.10.11)") | |
| nodes['PC3'] = create_pc(project_id, "PC3-Eng", 250, 150) | |
| if nodes['PC3']: print(" β PC3-Eng (10.1.20.10)") | |
| nodes['PC4'] = create_pc(project_id, "PC4-Eng", 450, 150) | |
| if nodes['PC4']: print(" β PC4-Eng (10.1.20.11)") | |
| # Wait for nodes to be ready | |
| import time | |
| print("\nβ±οΈ Waiting 2 seconds for nodes to initialize...") | |
| time.sleep(2) | |
| # Refresh node data | |
| resp = requests.get(f"{GNS3_API}/projects/{project_id}/nodes") | |
| all_nodes = {n['name']: n for n in resp.json()} | |
| for name in nodes.keys(): | |
| if name in ['SW1', 'SW2', 'SW3']: | |
| node_name = nodes[name]['name'] | |
| else: | |
| node_name = nodes[name]['name'] | |
| if node_name in all_nodes: | |
| nodes[name] = all_nodes[node_name] | |
| print("\nπ Creating Links...") | |
| links = 0 | |
| # PC1 β SW1 | |
| if nodes.get('PC1') and nodes.get('SW1'): | |
| if create_link(project_id, nodes['PC1']['node_id'], 0, 0, | |
| nodes['SW1']['node_id'], 0, 0, | |
| "PC1-Sales β SW1-Sales e0"): | |
| links += 1 | |
| # PC2 β SW1 | |
| if nodes.get('PC2') and nodes.get('SW1'): | |
| if create_link(project_id, nodes['PC2']['node_id'], 0, 0, | |
| nodes['SW1']['node_id'], 1, 0, | |
| "PC2-Sales β SW1-Sales e1"): | |
| links += 1 | |
| # SW1 β SW2 (SINGLE trunk - access to core) | |
| if nodes.get('SW1') and nodes.get('SW2'): | |
| if create_link(project_id, nodes['SW1']['node_id'], 15, 0, | |
| nodes['SW2']['node_id'], 0, 0, | |
| "SW1-Sales e15 β SW2-Core e0 (TRUNK)"): | |
| links += 1 | |
| # SW3 β SW2 (SINGLE trunk - access to core) | |
| if nodes.get('SW3') and nodes.get('SW2'): | |
| if create_link(project_id, nodes['SW3']['node_id'], 15, 0, | |
| nodes['SW2']['node_id'], 1, 0, | |
| "SW3-Eng e15 β SW2-Core e1 (TRUNK)"): | |
| links += 1 | |
| # PC3 β SW3 | |
| if nodes.get('PC3') and nodes.get('SW3'): | |
| if create_link(project_id, nodes['PC3']['node_id'], 0, 0, | |
| nodes['SW3']['node_id'], 0, 0, | |
| "PC3-Eng β SW3-Eng e0"): | |
| links += 1 | |
| # PC4 β SW3 | |
| if nodes.get('PC4') and nodes.get('SW3'): | |
| if create_link(project_id, nodes['PC4']['node_id'], 0, 0, | |
| nodes['SW3']['node_id'], 1, 0, | |
| "PC4-Eng β SW3-Eng e1"): | |
| links += 1 | |
| print("\n" + "="*70) | |
| print("β CLEAN CCNA TOPOLOGY COMPLETE!") | |
| print("="*70) | |
| print(f"\nπ Devices: {len([n for n in nodes.values() if n])}") | |
| print(f" - 3x Cisco IOSvL2 switches (with proper icons! π―)") | |
| print(f" - 4x VPCS (Virtual PCs)") | |
| print(f"π Links: {links} (NO redundant port-channels)") | |
| print("\nπ Topology Design:") | |
| print("```") | |
| print(" SW2-Core") | |
| print(" / \\") | |
| print(" SW1-Sales SW3-Eng") | |
| print(" / \\ / \\") | |
| print(" PC1 PC2 PC3 PC4") | |
| print(" (Sales) (Engineering)") | |
| print("```") | |
| print("\nπ VLAN Design:") | |
| print(" β’ SW1-Sales: VLAN 10 (10.1.10.0/24)") | |
| print(" β’ SW3-Eng: VLAN 20 (10.1.20.0/24)") | |
| print(" β’ Trunk ports: e15 on access, e0/e1 on core") | |
| print("\n⨠Open GNS3 GUI to see the clean topology with proper icons!") | |
| print("="*70) | |
| if __name__ == "__main__": | |
| main() | |