diff --git a/gns3server/controller/__init__.py b/gns3server/controller/__init__.py index 84e02ce9..33552c6a 100644 --- a/gns3server/controller/__init__.py +++ b/gns3server/controller/__init__.py @@ -30,6 +30,7 @@ except ImportError: from ..config import Config from ..utils import parse_version +from ..utils.images import default_images_directory from .project import Project from .template import Template @@ -72,6 +73,7 @@ class Controller: log.info("Controller is starting") self._install_base_configs() + self._install_builtin_disks() server_config = Config.instance().get_section_config("Server") Config.instance().listen_for_config_changes(self._update_config) host = server_config.get("host", "localhost") @@ -279,29 +281,50 @@ class Controller: except OSError as e: log.error(str(e)) + @staticmethod + def install_resource_files(dst_path, resource_name): + """ + Install files from resources to user's file system + """ + + if hasattr(sys, "frozen") and sys.platform.startswith("win"): + resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), resource_name)) + for filename in os.listdir(resource_path): + if not os.path.exists(os.path.join(dst_path, filename)): + shutil.copy(os.path.join(resource_path, filename), os.path.join(dst_path, filename)) + else: + for entry in importlib_resources.files(f'gns3server.{resource_name}').iterdir(): + full_path = os.path.join(dst_path, entry.name) + if entry.is_file() and not os.path.exists(full_path): + log.debug(f'Installing {resource_name} resource file "{entry.name}" to "{full_path}"') + shutil.copy(str(entry), os.path.join(dst_path, entry.name)) + def _install_base_configs(self): """ - At startup we copy base file to the user location to allow + At startup we copy base configs to the user location to allow them to customize it """ dst_path = self.configs_path() log.info(f"Installing base configs in '{dst_path}'") try: - if hasattr(sys, "frozen") and sys.platform.startswith("win"): - resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), "configs")) - for filename in os.listdir(resource_path): - if not os.path.exists(os.path.join(dst_path, filename)): - shutil.copy(os.path.join(resource_path, filename), os.path.join(dst_path, filename)) - else: - for entry in importlib_resources.files('gns3server.configs').iterdir(): - full_path = os.path.join(dst_path, entry.name) - if entry.is_file() and not os.path.exists(full_path): - log.debug(f"Installing base config file {entry.name} to {full_path}") - shutil.copy(str(entry), os.path.join(dst_path, entry.name)) + Controller.install_resource_files(dst_path, "configs") except OSError as e: log.error(f"Could not install base config files to {dst_path}: {e}") + def _install_builtin_disks(self): + """ + At startup we copy built-in Qemu disks to the user location to allow + them to use with appliances + """ + + dst_path = self.disks_path() + log.info(f"Installing built-in disks in '{dst_path}'") + try: + Controller.install_resource_files(dst_path, "disks") + except OSError as e: + log.error(f"Could not install disk files to {dst_path}: {e}") + def images_path(self): """ Get the image storage directory @@ -322,6 +345,15 @@ class Controller: os.makedirs(configs_path, exist_ok=True) return configs_path + def disks_path(self, emulator_type="qemu"): + """ + Get the disks storage directory + """ + + disks_path = default_images_directory(emulator_type) + os.makedirs(disks_path, exist_ok=True) + return disks_path + async def add_compute(self, compute_id=None, name=None, force=False, connect=True, **kwargs): """ Add a server to the dictionary of computes controlled by this controller diff --git a/gns3server/controller/appliance_manager.py b/gns3server/controller/appliance_manager.py index 15c4da70..679926f1 100644 --- a/gns3server/controller/appliance_manager.py +++ b/gns3server/controller/appliance_manager.py @@ -100,17 +100,9 @@ class ApplianceManager: dst_path = self._builtin_appliances_path(delete_first=True) log.info(f"Installing built-in appliances in '{dst_path}'") + from . import Controller try: - if hasattr(sys, "frozen") and sys.platform.startswith("win"): - resource_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable), "appliances")) - for filename in os.listdir(resource_path): - shutil.copy(os.path.join(resource_path, filename), os.path.join(dst_path, filename)) - else: - for entry in importlib_resources.files('gns3server.appliances').iterdir(): - full_path = os.path.join(dst_path, entry.name) - if entry.is_file(): - log.debug(f"Installing built-in appliance file {entry.name} to {full_path}") - shutil.copy(str(entry), os.path.join(dst_path, entry.name)) + Controller.instance().install_resource_files(dst_path, "appliances") except OSError as e: log.error(f"Could not install built-in appliance files to {dst_path}: {e}") diff --git a/gns3server/disks/empty100G.qcow2 b/gns3server/disks/empty100G.qcow2 new file mode 100644 index 00000000..94ed3e44 Binary files /dev/null and b/gns3server/disks/empty100G.qcow2 differ diff --git a/gns3server/disks/empty10G.qcow2 b/gns3server/disks/empty10G.qcow2 new file mode 100644 index 00000000..6a4d79b5 Binary files /dev/null and b/gns3server/disks/empty10G.qcow2 differ diff --git a/gns3server/disks/empty150G.qcow2 b/gns3server/disks/empty150G.qcow2 new file mode 100644 index 00000000..da508060 Binary files /dev/null and b/gns3server/disks/empty150G.qcow2 differ diff --git a/gns3server/disks/empty1T.qcow2 b/gns3server/disks/empty1T.qcow2 new file mode 100644 index 00000000..62e992ee Binary files /dev/null and b/gns3server/disks/empty1T.qcow2 differ diff --git a/gns3server/disks/empty200G.qcow2 b/gns3server/disks/empty200G.qcow2 new file mode 100644 index 00000000..ee7ce517 Binary files /dev/null and b/gns3server/disks/empty200G.qcow2 differ diff --git a/gns3server/disks/empty20G.qcow2 b/gns3server/disks/empty20G.qcow2 new file mode 100644 index 00000000..968f8fcf Binary files /dev/null and b/gns3server/disks/empty20G.qcow2 differ diff --git a/gns3server/disks/empty250G.qcow2 b/gns3server/disks/empty250G.qcow2 new file mode 100644 index 00000000..7be4641e Binary files /dev/null and b/gns3server/disks/empty250G.qcow2 differ diff --git a/gns3server/disks/empty30G.qcow2 b/gns3server/disks/empty30G.qcow2 new file mode 100644 index 00000000..414611bb Binary files /dev/null and b/gns3server/disks/empty30G.qcow2 differ diff --git a/gns3server/disks/empty40G.qcow2 b/gns3server/disks/empty40G.qcow2 new file mode 100644 index 00000000..74d53888 Binary files /dev/null and b/gns3server/disks/empty40G.qcow2 differ diff --git a/gns3server/disks/empty500G.qcow2 b/gns3server/disks/empty500G.qcow2 new file mode 100644 index 00000000..76d863a8 Binary files /dev/null and b/gns3server/disks/empty500G.qcow2 differ diff --git a/gns3server/disks/empty50G.qcow2 b/gns3server/disks/empty50G.qcow2 new file mode 100644 index 00000000..14366951 Binary files /dev/null and b/gns3server/disks/empty50G.qcow2 differ diff --git a/gns3server/disks/empty8G.qcow2 b/gns3server/disks/empty8G.qcow2 new file mode 100755 index 00000000..35f83ab7 Binary files /dev/null and b/gns3server/disks/empty8G.qcow2 differ diff --git a/gns3server/utils/images.py b/gns3server/utils/images.py index 93420069..2c5ed130 100644 --- a/gns3server/utils/images.py +++ b/gns3server/utils/images.py @@ -26,11 +26,11 @@ import logging log = logging.getLogger(__name__) -def list_images(type): +def list_images(emulator_type): """ - Scan directories for available image for a type + Scan directories for available image for a given emulator type - :param type: emulator type (dynamips, qemu, iou) + :param emulator_type: emulator type (dynamips, qemu, iou) """ files = set() images = [] @@ -38,10 +38,10 @@ def list_images(type): server_config = Config.instance().get_section_config("Server") general_images_directory = os.path.expanduser(server_config.get("images_path", "~/GNS3/images")) - # Subfolder of the general_images_directory specific to this VM type - default_directory = default_images_directory(type) + # Subfolder of the general_images_directory specific to this emulator type + default_directory = default_images_directory(emulator_type) - for directory in images_directories(type): + for directory in images_directories(emulator_type): # We limit recursion to path outside the default images directory # the reason is in the default directory manage file organization and @@ -57,9 +57,9 @@ def list_images(type): if filename not in files: if filename.endswith(".md5sum") or filename.startswith("."): continue - elif ((filename.endswith(".image") or filename.endswith(".bin")) and type == "dynamips") \ - or ((filename.endswith(".bin") or filename.startswith("i86bi")) and type == "iou") \ - or (not filename.endswith(".bin") and not filename.endswith(".image") and type == "qemu"): + elif ((filename.endswith(".image") or filename.endswith(".bin")) and emulator_type == "dynamips") \ + or ((filename.endswith(".bin") or filename.startswith("i86bi")) and emulator_type == "iou") \ + or (not filename.endswith(".bin") and not filename.endswith(".image") and emulator_type == "qemu"): files.add(filename) # It the image is located in the standard directory the path is relative @@ -69,7 +69,7 @@ def list_images(type): path = os.path.relpath(os.path.join(root, filename), default_directory) try: - if type in ["dynamips", "iou"]: + if emulator_type in ["dynamips", "iou"]: with open(os.path.join(root, filename), "rb") as f: # read the first 7 bytes of the file. elf_header_start = f.read(7) @@ -102,34 +102,34 @@ def _os_walk(directory, recurse=True, **kwargs): yield directory, [], files -def default_images_directory(type): +def default_images_directory(emulator_type): """ :returns: Return the default directory for a node type """ server_config = Config.instance().get_section_config("Server") img_dir = os.path.expanduser(server_config.get("images_path", "~/GNS3/images")) - if type == "qemu": + if emulator_type == "qemu": return os.path.join(img_dir, "QEMU") - elif type == "iou": + elif emulator_type == "iou": return os.path.join(img_dir, "IOU") - elif type == "dynamips": + elif emulator_type == "dynamips": return os.path.join(img_dir, "IOS") else: - raise NotImplementedError("%s node type is not supported", type) + raise NotImplementedError("%s node type is not supported", emulator_type) -def images_directories(type): +def images_directories(emulator_type): """ Return all directories where we will look for images by priority - :param type: Type of emulator + :param emulator_type: Type of emulator """ server_config = Config.instance().get_section_config("Server") paths = [] img_dir = os.path.expanduser(server_config.get("images_path", "~/GNS3/images")) - type_img_directory = default_images_directory(type) + type_img_directory = default_images_directory(emulator_type) try: os.makedirs(type_img_directory, exist_ok=True) paths.append(type_img_directory) diff --git a/tests/controller/test_controller.py b/tests/controller/test_controller.py index 198556a1..86e49109 100644 --- a/tests/controller/test_controller.py +++ b/tests/controller/test_controller.py @@ -395,6 +395,31 @@ async def test_install_base_configs(controller, config, tmpdir): assert f.read() == 'test' +@pytest.mark.parametrize( + "builtin_disk", + [ + "empty8G.qcow2", + "empty10G.qcow2", + "empty20G.qcow2", + "empty30G.qcow2", + "empty40G.qcow2", + "empty50G.qcow2", + "empty100G.qcow2", + "empty150G.qcow2", + "empty200G.qcow2", + "empty250G.qcow2", + "empty500G.qcow2", + "empty1T.qcow2" + ] +) +async def test_install_builtin_disks(controller, config, tmpdir, builtin_disk): + + config.set_section_config("Server", {"images_path": str(tmpdir)}) + controller._install_builtin_disks() + # we only install Qemu empty disks at this time + assert os.path.exists(str(tmpdir / "QEMU" / builtin_disk)) + + def test_appliances(controller, tmpdir): my_appliance = {