""" GNS3 Lab Viewer Integration Provides URLs and status for viewing deployed network configurations in real-time. """ import requests from typing import Dict, List, Optional from dataclasses import dataclass import logging logger = logging.getLogger(__name__) # Public GNS3 lab endpoint GNS3_BASE_URL = "http://lab.grahampaasch.com:3080" GNS3_PROJECT_ID = "1f712057-b33d-433f-90bb-2b9b3804a95e" GNS3_PROJECT_NAME = "overgrowth" @dataclass class DeviceStatus: """Status of a deployed network device""" name: str status: str # started, stopped, suspended console_port: int node_type: str console_url: str deployed: bool = False class GNS3Viewer: """Interface to GNS3 lab for viewing deployed configurations""" def __init__(self, base_url: str = GNS3_BASE_URL, project_id: str = GNS3_PROJECT_ID): self.base_url = base_url self.project_id = project_id self.api_base = f"{base_url}/v2" def get_web_ui_url(self) -> str: """Get the direct URL to view the project in GNS3 web UI""" return f"{self.base_url}/static/web-ui/server/1/project/{self.project_id}" def is_accessible(self) -> bool: """Check if GNS3 server is accessible""" try: response = requests.get(f"{self.api_base}/version", timeout=5) return response.status_code == 200 except Exception as e: logger.error(f"GNS3 server not accessible: {e}") return False def get_project_info(self) -> Optional[Dict]: """Get project information""" try: response = requests.get(f"{self.api_base}/projects/{self.project_id}", timeout=5) if response.status_code == 200: return response.json() return None except Exception as e: logger.error(f"Failed to get project info: {e}") return None def get_devices(self, device_filter: Optional[str] = None) -> List[DeviceStatus]: """ Get list of devices in the project Args: device_filter: Optional name filter (e.g., "SW-" for switches) Returns: List of DeviceStatus objects """ try: response = requests.get(f"{self.api_base}/projects/{self.project_id}/nodes", timeout=5) if response.status_code != 200: return [] nodes = response.json() devices = [] for node in nodes: if device_filter and device_filter not in node.get('name', ''): continue device = DeviceStatus( name=node.get('name', 'Unknown'), status=node.get('status', 'unknown'), console_port=node.get('console', 0), node_type=node.get('node_type', 'unknown'), console_url=f"telnet://lab.grahampaasch.com:{node.get('console', 0)}", deployed=node.get('status') == 'started' ) devices.append(device) return devices except Exception as e: logger.error(f"Failed to get devices: {e}") return [] def get_deployment_summary(self) -> Dict: """ Get a summary of the current deployment status Returns: Dictionary with deployment statistics and URLs """ devices = self.get_devices() switches = [d for d in devices if 'SW-' in d.name] routers = [d for d in devices if 'R-' in d.name or 'R1' in d.name or 'R2' in d.name] deployed_switches = [s for s in switches if s.deployed] deployed_routers = [r for r in routers if r.deployed] return { 'web_ui_url': self.get_web_ui_url(), 'accessible': self.is_accessible(), 'total_devices': len(devices), 'switches': { 'total': len(switches), 'deployed': len(deployed_switches), 'devices': [{'name': s.name, 'status': s.status, 'console': s.console_url} for s in deployed_switches] }, 'routers': { 'total': len(routers), 'deployed': len(deployed_routers), 'devices': [{'name': r.name, 'status': r.status, 'console': r.console_url} for r in deployed_routers] }, 'project': { 'id': self.project_id, 'name': GNS3_PROJECT_NAME, 'api_url': f"{self.api_base}/projects/{self.project_id}" } } def get_device_console_output(self, device_name: str, lines: int = 50) -> Optional[str]: """ Get recent console output from a device (if available via API) Note: GNS3 doesn't provide console history via API by default, but this method is a placeholder for future enhancement. """ # This would require additional GNS3 server configuration # For now, users should use telnet to console ports return None def format_for_ui(self) -> str: """ Format deployment summary as markdown for display in HuggingFace Space Returns: Markdown formatted string """ summary = self.get_deployment_summary() if not summary['accessible']: return "⚠️ **GNS3 Lab is currently not accessible**" md = f""" ## 🌐 Live Network Lab View **[📡 Open GNS3 Web UI]({summary['web_ui_url']})** - View live network topology ### Deployment Status **Total Devices:** {summary['total_devices']} #### Switches ({summary['switches']['deployed']}/{summary['switches']['total']} deployed) """ for switch in summary['switches']['devices']: md += f"- ✅ **{switch['name']}** - Status: `{switch['status']}` - Console: `{switch['console']}`\n" if summary['routers']['deployed'] > 0: md += f"\n#### Routers ({summary['routers']['deployed']}/{summary['routers']['total']} deployed)\n" for router in summary['routers']['devices']: md += f"- ✅ **{router['name']}** - Status: `{router['status']}` - Console: `{router['console']}`\n" md += f""" ### How to Access 1. **Web UI:** Click the link above to view the live topology 2. **Console Access:** Use telnet to the console URLs shown above 3. **API Access:** `{summary['project']['api_url']}` ### Project Information - **Name:** {summary['project']['name']} - **ID:** `{summary['project']['id']}` """ return md def get_deployment_status() -> Dict: """ Convenience function to get deployment status Returns: Dictionary with deployment information """ viewer = GNS3Viewer() return viewer.get_deployment_summary() def get_web_ui_link() -> str: """ Get the direct link to view deployed network in GNS3 web UI Returns: URL string """ viewer = GNS3Viewer() return viewer.get_web_ui_url() def get_deployment_markdown() -> str: """ Get markdown-formatted deployment status for HuggingFace Space UI Returns: Markdown string """ viewer = GNS3Viewer() return viewer.format_for_ui() if __name__ == "__main__": # Test the viewer import json viewer = GNS3Viewer() print("=" * 60) print("GNS3 Lab Viewer - Deployment Status") print("=" * 60) print() # Check accessibility if viewer.is_accessible(): print("✅ GNS3 lab is accessible at", GNS3_BASE_URL) else: print("❌ GNS3 lab is not accessible") exit(1) print() print("Web UI URL:", viewer.get_web_ui_url()) print() # Get deployment summary summary = viewer.get_deployment_summary() print("Deployment Summary:") print(json.dumps(summary, indent=2)) print() # Get markdown output print("=" * 60) print("Markdown Output for HuggingFace Space:") print("=" * 60) print() print(viewer.format_for_ui())