""" GNS3 File Index Manager This module provides functionality to manage an index of files in GNS3 projects. Since GNS3 API doesn't provide a way to list files, we maintain an index file to track files written to each project. """ import json import logging from datetime import datetime from typing import Any from gns3_copilot.gns3_client import Project, get_gns3_connector # Configure logging logger = logging.getLogger(__name__) # Load environment variables # Index file name (stored in project directory) INDEX_FILE_NAME = ".gns3_copilot_file_index.json" def _get_index_path(project_id: str) -> str: """ Get the path to the index file for a project. Args: project_id: The project ID Returns: The path to the index file (relative to project directory) """ return INDEX_FILE_NAME def load_file_index(project: Project) -> dict[str, Any]: """ Load the file index for a project. Args: project: GNS3 Project instance Returns: A dictionary containing the index data, or an empty index if not found """ if project.project_id is None: raise ValueError("Project ID must be set") index_path = _get_index_path(project.project_id) try: content = project.get_file(path=index_path) index_data = json.loads(content) # Validate index structure if not isinstance(index_data, dict): logger.warning("Invalid index structure, creating new index") return _create_empty_index(project.project_id) if "files" not in index_data: index_data["files"] = [] logger.info( "Loaded file index for project %s with %d files", project.project_id, len(index_data["files"]), ) return index_data except Exception as e: logger.info( "No existing file index found for project %s, creating new one: %s", project.project_id, e, ) return _create_empty_index(project.project_id) def save_file_index(project: Project, index_data: dict[str, Any]) -> None: """ Save the file index for a project. Args: project: GNS3 Project instance index_data: The index data to save Raises: ValueError: If failed to save the index """ if project.project_id is None: raise ValueError("Project ID must be set") index_path = _get_index_path(project.project_id) try: # Ensure project_id is in the index index_data["project_id"] = project.project_id # Save as JSON with indentation for readability content = json.dumps(index_data, indent=2, ensure_ascii=False) project.write_file(path=index_path, data=content) logger.info("Saved file index for project %s", project.project_id) except Exception as e: logger.error( "Failed to save file index for project %s: %s", project.project_id, e ) raise ValueError(f"Failed to save file index: {str(e)}") from e def _create_empty_index(project_id: str) -> dict[str, Any]: """ Create an empty file index structure. Args: project_id: The project ID Returns: A dictionary with empty index structure """ return { "project_id": project_id, "files": [], "created_at": datetime.now().isoformat(), "updated_at": datetime.now().isoformat(), } def add_file_to_index( project: Project, path: str, size: int | None = None, ) -> dict[str, Any]: """ Add or update a file in the project index. Args: project: GNS3 Project instance path: The file path size: Optional file size in bytes Returns: The updated index data Raises: ValueError: If failed to update the index """ try: # Load existing index index_data = load_file_index(project) # Check if file already exists file_entry = next((f for f in index_data["files"] if f["path"] == path), None) now = datetime.now().isoformat() if file_entry: # Update existing file file_entry["updated_at"] = now if size is not None: file_entry["size"] = size logger.info("Updated file in index: %s", path) else: # Add new file new_entry: dict[str, Any] = { "path": path, "created_at": now, "updated_at": None, } if size is not None: new_entry["size"] = size index_data["files"].append(new_entry) logger.info("Added file to index: %s", path) # Update index timestamp index_data["updated_at"] = now # Save index save_file_index(project, index_data) return index_data except Exception as e: logger.error("Failed to add file to index: %s", e) raise ValueError(f"Failed to add file to index: {str(e)}") from e def get_file_list(project: Project) -> list[dict[str, Any]]: """ Get the list of files from the project index. Args: project: GNS3 Project instance Returns: A list of file entries """ try: index_data = load_file_index(project) files = index_data.get("files", []) return files if isinstance(files, list) else [] except Exception as e: logger.error("Failed to get file list: %s", e) return [] if __name__ == "__main__": # Test the file index manager print("=" * 80) print("Testing GNS3 File Index Manager") print("=" * 80) try: # Create connector and project connector = get_gns3_connector() if connector is None: print("ERROR: Failed to create GNS3 connector") exit(1) # Replace with actual project ID for testing test_project_id = "your-project-uuid" print(f"\nTesting with project: {test_project_id}") print("NOTE: Replace 'your-project-uuid' with actual project ID") # Create project instance project = Project(project_id=test_project_id, connector=connector) # Load index print("\n1. Loading file index...") index_data = load_file_index(project) print(f" Index loaded: {len(index_data.get('files', []))} files") # Add file to index print("\n2. Adding file to index...") updated_index = add_file_to_index(project, "test_file.txt", size=1024) print(f" File added. Total files: {len(updated_index['files'])}") # Get file list print("\n3. Getting file list...") file_list = get_file_list(project) print(" Files in index:") for file_entry in file_list: print(f" - {file_entry['path']}") print("\n" + "=" * 80) print("Test completed successfully!") print("=" * 80) except Exception as e: print(f"\nERROR: {e}") import traceback traceback.print_exc()