From aa2472fb30808a79c14effe23aae3e8a2b8b5593 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 14 Apr 2015 18:46:55 +0200 Subject: [PATCH] Rewrote image search This code is more generic and support all cases. Previously we had bug where the user lost his image path if the image was not located in image directory. --- gns3server/modules/base_manager.py | 37 +++++++++++ gns3server/modules/dynamips/__init__.py | 6 ++ gns3server/modules/dynamips/nodes/router.py | 9 +-- gns3server/modules/iou/__init__.py | 6 ++ gns3server/modules/iou/iou_vm.py | 15 +---- gns3server/modules/qemu/__init__.py | 6 ++ gns3server/modules/qemu/qemu_vm.py | 68 ++++++--------------- tests/modules/iou/test_iou_manager.py | 16 +++++ tests/modules/test_manager.py | 31 ++++++++++ 9 files changed, 124 insertions(+), 70 deletions(-) diff --git a/gns3server/modules/base_manager.py b/gns3server/modules/base_manager.py index 9238aaad..d6097fce 100644 --- a/gns3server/modules/base_manager.py +++ b/gns3server/modules/base_manager.py @@ -371,3 +371,40 @@ class BaseManager: nio = NIOGenericEthernet(nio_settings["ethernet_device"]) assert nio is not None return nio + + def get_abs_image_path(self, path): + """ + Get the absolute path of an image + + :param path: file path + :return: file path + """ + + img_directory = self.get_images_directory() + + if not os.path.isabs(path): + s = os.path.split(path) + return os.path.normpath(os.path.join(img_directory, *s)) + return path + + def get_relative_image_path(self, path): + """ + Get a path relative to images directory path + or an abspath if the path is not located inside + image directory + + :param path: file path + :return: file path + """ + + img_directory = self.get_images_directory() + path = self.get_abs_image_path(path) + if os.path.dirname(path) == img_directory: + return os.path.basename(path) + return path + + def get_images_directory(self): + """ + Get the image directory on disk + """ + raise NotImplementedError diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 1592e9ce..ed6858a8 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -616,3 +616,9 @@ class Dynamips(BaseManager): if was_auto_started: yield from vm.stop() return validated_idlepc + + def get_images_directory(self): + """ + Return the full path of the images directory on disk + """ + return os.path.join(os.path.expanduser(self.config.get_section_config("Server").get("images_path", "~/GNS3/images")), "IOS") diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py index 568a11f6..6a1291b0 100644 --- a/gns3server/modules/dynamips/nodes/router.py +++ b/gns3server/modules/dynamips/nodes/router.py @@ -151,10 +151,7 @@ class Router(BaseVM): "system_id": self._system_id} # return the relative path if the IOS image is in the images_path directory - server_config = self.manager.config.get_section_config("Server") - relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOS", self._image) - if os.path.exists(relative_image): - router_info["image"] = os.path.basename(self._image) + router_info["image"] = self.manager.get_relative_image_path(self._image) # add the slots slot_number = 0 @@ -427,9 +424,7 @@ class Router(BaseVM): :param image: path to IOS image file """ - if not os.path.isabs(image): - server_config = self.manager.config.get_section_config("Server") - image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOS", image) + image = self.manager.get_abs_image_path(image) if not os.path.isfile(image): raise DynamipsError("IOS image '{}' is not accessible".format(image)) diff --git a/gns3server/modules/iou/__init__.py b/gns3server/modules/iou/__init__.py index 764d6475..df6ad3c7 100644 --- a/gns3server/modules/iou/__init__.py +++ b/gns3server/modules/iou/__init__.py @@ -91,3 +91,9 @@ class IOU(BaseManager): """ return os.path.join("iou", "device-{}".format(legacy_vm_id)) + + def get_images_directory(self): + """ + Return the full path of the images directory on disk + """ + return os.path.join(os.path.expanduser(self.config.get_section_config("Server").get("images_path", "~/GNS3/images")), "IOU") diff --git a/gns3server/modules/iou/iou_vm.py b/gns3server/modules/iou/iou_vm.py index d8d47a33..2b7efd69 100644 --- a/gns3server/modules/iou/iou_vm.py +++ b/gns3server/modules/iou/iou_vm.py @@ -145,14 +145,7 @@ class IOUVM(BaseVM): :param path: path to the IOU image executable """ - if not os.path.isabs(path): - server_config = self.manager.config.get_section_config("Server") - relative_path = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), path) - if not os.path.exists(relative_path): - relative_path = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOU", path) - path = relative_path - - self._path = path + self._path = self.manager.get_abs_image_path(path) # In 1.2 users uploaded images to the images roots # after the migration their images are inside images/IOU @@ -240,11 +233,7 @@ class IOUVM(BaseVM): "iourc_path": self.iourc_path} # return the relative path if the IOU image is in the images_path directory - server_config = self.manager.config.get_section_config("Server") - relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "IOU", self.path) - if os.path.exists(relative_image): - iou_vm_info["path"] = os.path.basename(self.path) - + iou_vm_info["path"] = self.manager.get_relative_image_path(self.path) return iou_vm_info @property diff --git a/gns3server/modules/qemu/__init__.py b/gns3server/modules/qemu/__init__.py index d348894f..e5bf698a 100644 --- a/gns3server/modules/qemu/__init__.py +++ b/gns3server/modules/qemu/__init__.py @@ -112,3 +112,9 @@ class Qemu(BaseManager): """ return os.path.join("qemu", "vm-{}".format(legacy_vm_id)) + + def get_images_directory(self): + """ + Return the full path of the images directory on disk + """ + return os.path.join(os.path.expanduser(self.config.get_section_config("Server").get("images_path", "~/GNS3/images")), "QEMU") diff --git a/gns3server/modules/qemu/qemu_vm.py b/gns3server/modules/qemu/qemu_vm.py index 75731d24..420bd5e0 100644 --- a/gns3server/modules/qemu/qemu_vm.py +++ b/gns3server/modules/qemu/qemu_vm.py @@ -148,14 +148,10 @@ class QemuVM(BaseVM): :param hda_disk_image: QEMU hda disk image path """ - if not os.path.isabs(hda_disk_image): - server_config = self.manager.config.get_section_config("Server") - hda_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hda_disk_image) - + self._hda_disk_image = self.manager.get_abs_image_path(hda_disk_image) log.info('QEMU VM "{name}" [{id}] has set the QEMU hda disk image path to {disk_image}'.format(name=self._name, id=self._id, - disk_image=hda_disk_image)) - self._hda_disk_image = hda_disk_image + disk_image=self._hda_disk_image)) @property def hdb_disk_image(self): @@ -175,14 +171,10 @@ class QemuVM(BaseVM): :param hdb_disk_image: QEMU hdb disk image path """ - if not os.path.isabs(hdb_disk_image): - server_config = self.manager.config.get_section_config("Server") - hdb_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdb_disk_image) - + self._hdb_disk_image = self.manager.get_abs_image_path(hdb_disk_image) log.info('QEMU VM "{name}" [{id}] has set the QEMU hdb disk image path to {disk_image}'.format(name=self._name, id=self._id, - disk_image=hdb_disk_image)) - self._hdb_disk_image = hdb_disk_image + disk_image=self._hdb_disk_image)) @property def hdc_disk_image(self): @@ -202,14 +194,10 @@ class QemuVM(BaseVM): :param hdc_disk_image: QEMU hdc disk image path """ - if not os.path.isabs(hdc_disk_image): - server_config = self.manager.config.get_section_config("Server") - hdc_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdc_disk_image) - + self._hdc_disk_image = self.manager.get_abs_image_path(hdc_disk_image) log.info('QEMU VM "{name}" [{id}] has set the QEMU hdc disk image path to {disk_image}'.format(name=self._name, id=self._id, - disk_image=hdc_disk_image)) - self._hdc_disk_image = hdc_disk_image + disk_image=self._hdc_disk_image)) @property def hdd_disk_image(self): @@ -229,14 +217,11 @@ class QemuVM(BaseVM): :param hdd_disk_image: QEMU hdd disk image path """ - if not os.path.isabs(hdd_disk_image): - server_config = self.manager.config.get_section_config("Server") - hdd_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdd_disk_image) - + self._hdd_disk_image = hdd_disk_image + self._hdd_disk_image = self.manager.get_abs_image_path(hdd_disk_image) log.info('QEMU VM "{name}" [{id}] has set the QEMU hdd disk image path to {disk_image}'.format(name=self._name, id=self._id, - disk_image=hdd_disk_image)) - self._hdd_disk_image = hdd_disk_image + disk_image=self._hdd_disk_image)) @property def adapters(self): @@ -778,9 +763,9 @@ class QemuVM(BaseVM): adapter.add_nio(0, nio) log.info('QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format(name=self._name, - id=self._id, - nio=nio, - adapter_number=adapter_number)) + id=self._id, + nio=nio, + adapter_number=adapter_number)) @asyncio.coroutine def adapter_remove_nio_binding(self, adapter_number): @@ -1068,23 +1053,6 @@ class QemuVM(BaseVM): command.extend(self._graphic()) return command - def _get_relative_disk_image_path(self, disk_image): - """ - Returns a relative image path if the disk image is in the images directory. - - :param disk_image: path to the disk image - - :returns: relative or full path - """ - - if disk_image: - # return the relative path if the disk image is in the images_path directory - server_config = self.manager.config.get_section_config("Server") - relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", disk_image) - if os.path.exists(relative_image): - return os.path.basename(disk_image) - return disk_image - def __json__(self): answer = { "project_id": self.project.id, @@ -1095,11 +1063,11 @@ class QemuVM(BaseVM): if field not in answer: answer[field] = getattr(self, field) - answer["hda_disk_image"] = self._get_relative_disk_image_path(self._hda_disk_image) - answer["hdb_disk_image"] = self._get_relative_disk_image_path(self._hdb_disk_image) - answer["hdc_disk_image"] = self._get_relative_disk_image_path(self._hdc_disk_image) - answer["hdd_disk_image"] = self._get_relative_disk_image_path(self._hdd_disk_image) - answer["initrd"] = self._get_relative_disk_image_path(self._initrd) - answer["kernel_image"] = self._get_relative_disk_image_path(self._kernel_image) + answer["hda_disk_image"] = self.manager.get_relative_image_path(self._hda_disk_image) + answer["hdb_disk_image"] = self.manager.get_relative_image_path(self._hdb_disk_image) + answer["hdc_disk_image"] = self.manager.get_relative_image_path(self._hdc_disk_image) + answer["hdd_disk_image"] = self.manager.get_relative_image_path(self._hdd_disk_image) + answer["initrd"] = self.manager.get_relative_image_path(self._initrd) + answer["kernel_image"] = self.manager.get_relative_image_path(self._kernel_image) return answer diff --git a/tests/modules/iou/test_iou_manager.py b/tests/modules/iou/test_iou_manager.py index 7817a297..84b3b7e2 100644 --- a/tests/modules/iou/test_iou_manager.py +++ b/tests/modules/iou/test_iou_manager.py @@ -17,7 +17,9 @@ import pytest +from unittest.mock import patch import uuid +import os from gns3server.modules.iou import IOU @@ -25,6 +27,15 @@ from gns3server.modules.iou.iou_error import IOUError from gns3server.modules.project_manager import ProjectManager +@pytest.fixture(scope="function") +def iou(port_manager): + # Cleanup the IOU object + IOU._instance = None + iou = IOU.instance() + iou.port_manager = port_manager + return iou + + def test_get_application_id(loop, project, port_manager): # Cleanup the IOU object IOU._instance = None @@ -71,3 +82,8 @@ def test_get_application_id_no_id_available(loop, project, port_manager): vm_id = str(uuid.uuid4()) loop.run_until_complete(iou.create_vm("PC {}".format(i), project.id, vm_id)) assert iou.get_application_id(vm_id) == i + + +def test_get_images_directory(iou, tmpdir): + with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}): + assert iou.get_images_directory() == str(tmpdir / "IOU") diff --git a/tests/modules/test_manager.py b/tests/modules/test_manager.py index a9d32daf..e43b1d87 100644 --- a/tests/modules/test_manager.py +++ b/tests/modules/test_manager.py @@ -22,6 +22,7 @@ from unittest.mock import patch from gns3server.modules.vpcs import VPCS +from gns3server.modules.iou import IOU def test_create_vm_new_topology(loop, project, port_manager): @@ -82,3 +83,33 @@ def test_create_vm_old_topology(loop, project, tmpdir, port_manager): vm_dir = os.path.join(project_dir, "project-files", "vpcs", vm.id) with open(os.path.join(vm_dir, "startup.vpc")) as f: assert f.read() == "1" + + +def test_get_abs_image_path(iou, tmpdir): + os.makedirs(str(tmpdir / "IOU")) + path1 = str(tmpdir / "test1.bin") + open(path1, 'w+').close() + + path2 = str(tmpdir / "IOU" / "test2.bin") + open(path2, 'w+').close() + + with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}): + assert iou.get_abs_image_path(path1) == path1 + assert iou.get_abs_image_path(path2) == path2 + assert iou.get_abs_image_path("test2.bin") == path2 + assert iou.get_abs_image_path("../test1.bin") == path1 + + +def test_get_relative_image_path(iou, tmpdir): + os.makedirs(str(tmpdir / "IOU")) + path1 = str(tmpdir / "test1.bin") + open(path1, 'w+').close() + + path2 = str(tmpdir / "IOU" / "test2.bin") + open(path2, 'w+').close() + + with patch("gns3server.config.Config.get_section_config", return_value={"images_path": str(tmpdir)}): + assert iou.get_relative_image_path(path1) == path1 + assert iou.get_relative_image_path(path2) == "test2.bin" + assert iou.get_relative_image_path("test2.bin") == "test2.bin" + assert iou.get_relative_image_path("../test1.bin") == path1