diff --git a/conf/gns3_server.conf b/conf/gns3_server.conf index 366ac89e..0b628b4b 100644 --- a/conf/gns3_server.conf +++ b/conf/gns3_server.conf @@ -57,6 +57,9 @@ default_symbol_theme = Affinity-square-blue ; Option to enable or disable raw images to be uploaded to the server allow_raw_images = True +; Option to automatically discover images in the images directory +auto_discover_images = True + ; Option to automatically send crash reports to the GNS3 team report_errors = True diff --git a/gns3server/core/tasks.py b/gns3server/core/tasks.py index 6d363baa..24de5940 100644 --- a/gns3server/core/tasks.py +++ b/gns3server/core/tasks.py @@ -21,6 +21,7 @@ from typing import Callable from fastapi import FastAPI from gns3server.controller import Controller +from gns3server.config import Config from gns3server.compute import MODULES from gns3server.compute.port_manager import PortManager from gns3server.utils.http_client import HTTPClient @@ -60,9 +61,10 @@ def create_startup_handler(app: FastAPI) -> Callable: # computing with server start from gns3server.compute.qemu import Qemu - # Start the discovering new images on file system 5 seconds after the server has started - # to give it a chance to process API requests - loop.call_later(5, asyncio.create_task, discover_images_on_filesystem(app)) + if Config.instance().settings.Server.auto_discover_images is True: + # Start the discovering new images on file system 5 seconds after the server has started + # to give it a chance to process API requests + loop.call_later(5, asyncio.create_task, discover_images_on_filesystem(app)) for module in MODULES: log.debug(f"Loading module {module.__name__}") diff --git a/gns3server/db/tasks.py b/gns3server/db/tasks.py index a31210a9..3b270b0d 100644 --- a/gns3server/db/tasks.py +++ b/gns3server/db/tasks.py @@ -30,7 +30,7 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from gns3server.db.repositories.computes import ComputesRepository from gns3server.db.repositories.images import ImagesRepository -from gns3server.utils.images import discover_images, check_valid_image_header, read_image_info, InvalidImageError +from gns3server.utils.images import discover_images, check_valid_image_header, read_image_info, default_images_directory, InvalidImageError from gns3server import schemas from .models import Base @@ -117,12 +117,16 @@ def image_filter(change: Change, path: str) -> bool: async def monitor_images_on_filesystem(app: FastAPI): - server_config = Config.instance().settings.Server - images_dir = os.path.expanduser(server_config.images_path) + directories_to_monitor = [] + for image_type in ("qemu", "ios", "iou"): + image_dir = default_images_directory(image_type) + if os.path.isdir(image_dir): + log.debug(f"Monitoring for new images in '{image_dir}'") + directories_to_monitor.append(image_dir) try: async for changes in awatch( - images_dir, + *directories_to_monitor, watch_filter=image_filter, raise_interrupt=True ): diff --git a/gns3server/schemas/config.py b/gns3server/schemas/config.py index 6e99140a..b09be55a 100644 --- a/gns3server/schemas/config.py +++ b/gns3server/schemas/config.py @@ -137,6 +137,7 @@ class ServerSettings(BaseModel): configs_path: str = "~/GNS3/configs" default_symbol_theme: BuiltinSymbolTheme = BuiltinSymbolTheme.affinity_square_blue allow_raw_images: bool = True + auto_discover_images: bool = True report_errors: bool = True additional_images_paths: List[str] = Field(default_factory=list) console_start_port_range: int = Field(5000, gt=0, le=65535) diff --git a/gns3server/utils/images.py b/gns3server/utils/images.py index 8e5a48a2..5d7f0c9c 100644 --- a/gns3server/utils/images.py +++ b/gns3server/utils/images.py @@ -136,7 +136,8 @@ async def discover_images(image_type: str, skip_image_paths: list = None) -> Lis files = set() images = [] - for directory in images_directories(image_type): + for directory in images_directories(image_type, include_parent_directory=False): + log.info(f"Discovering images in '{directory}'") for root, _, filenames in os.walk(os.path.normpath(directory)): for filename in filenames: if filename.endswith(".tmp") or filename.endswith(".md5sum") or filename.startswith("."): @@ -189,7 +190,7 @@ def default_images_directory(image_type): raise NotImplementedError(f"%s node type is not supported", image_type) -def images_directories(image_type): +def images_directories(image_type, include_parent_directory=True): """ Return all directories where we will look for images by priority @@ -199,7 +200,7 @@ def images_directories(image_type): server_config = Config.instance().settings.Server paths = [] - img_dir = os.path.expanduser(server_config.images_path) + type_img_directory = default_images_directory(image_type) try: os.makedirs(type_img_directory, exist_ok=True) @@ -208,8 +209,10 @@ def images_directories(image_type): pass for directory in server_config.additional_images_paths: paths.append(directory) - # Compatibility with old topologies we look in parent directory - paths.append(img_dir) + if include_parent_directory: + # Compatibility with old topologies we look in parent directory + img_dir = os.path.expanduser(server_config.images_path) + paths.append(img_dir) # Return only the existing paths return [force_unix_path(p) for p in paths if os.path.exists(p)]