From 3497deaa31ab4d29b579a89d0fa7186d2e529108 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 19 Nov 2018 15:53:43 +0700 Subject: [PATCH 1/2] Allow virtual machines to use files in project directory as disk images. --- gns3server/compute/base_manager.py | 30 ++++++++++++++------- gns3server/compute/dynamips/nodes/router.py | 5 ++-- gns3server/compute/iou/iou_vm.py | 7 +++-- gns3server/compute/qemu/qemu_vm.py | 30 ++++++++++----------- tests/handlers/api/compute/test_qemu.py | 11 ++++++++ 5 files changed, 52 insertions(+), 31 deletions(-) diff --git a/gns3server/compute/base_manager.py b/gns3server/compute/base_manager.py index 5e9a0d50..cccab216 100644 --- a/gns3server/compute/base_manager.py +++ b/gns3server/compute/base_manager.py @@ -469,12 +469,14 @@ class BaseManager: except PermissionError: raise aiohttp.web.HTTPForbidden() - def get_abs_image_path(self, path): + def get_abs_image_path(self, path, extra_dir=None): """ Get the absolute path of an image :param path: file path - :return: file path + :param extra_dir: an additional directory to be added to the search path + + :returns: file path """ if not path: @@ -483,6 +485,9 @@ class BaseManager: server_config = self.config.get_section_config("Server") img_directory = self.get_images_directory() + valid_directory_prefices = images_directories(self._NODE_TYPE) + if extra_dir: + valid_directory_prefices.append(extra_dir) # Windows path should not be send to a unix server if not sys.platform.startswith("win"): @@ -490,7 +495,7 @@ class BaseManager: raise NodeError("{} is not allowed on this remote server. Please use only a filename in {}.".format(path, img_directory)) if not os.path.isabs(path): - for directory in images_directories(self._NODE_TYPE): + for directory in valid_directory_prefices: path = self._recursive_search_file_in_directory(directory, orig_path) if path: return force_unix_path(path) @@ -502,15 +507,16 @@ class BaseManager: return path raise ImageMissingError(orig_path) - # For non local server we disallow using absolute path outside image directory + # For local server we allow using absolute path outside image directory if server_config.getboolean("local", False) is True: path = force_unix_path(path) if os.path.exists(path): return path raise ImageMissingError(orig_path) + # Check to see if path is an absolute path to a valid directory path = force_unix_path(path) - for directory in images_directories(self._NODE_TYPE): + for directory in valid_directory_prefices: if os.path.commonprefix([directory, path]) == directory: if os.path.exists(path): return path @@ -534,23 +540,29 @@ class BaseManager: return path return None - def get_relative_image_path(self, path): + def get_relative_image_path(self, path, extra_dir=None): """ 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 + :param extra_dir: an additional directory to be added to the search path + + :returns: file path """ if not path: return "" - path = force_unix_path(self.get_abs_image_path(path)) + path = force_unix_path(self.get_abs_image_path(path, extra_dir)) img_directory = self.get_images_directory() - for directory in images_directories(self._NODE_TYPE): + valid_directory_prefices = images_directories(self._NODE_TYPE) + if extra_dir: + valid_directory_prefices.append(extra_dir) + + for directory in valid_directory_prefices: if os.path.commonprefix([directory, path]) == directory: relpath = os.path.relpath(path, directory) # We don't allow to recurse search from the top image directory just for image type directory (compatibility with old releases) diff --git a/gns3server/compute/dynamips/nodes/router.py b/gns3server/compute/dynamips/nodes/router.py index 19aa36fe..eb4be4dd 100644 --- a/gns3server/compute/dynamips/nodes/router.py +++ b/gns3server/compute/dynamips/nodes/router.py @@ -169,8 +169,7 @@ class Router(BaseNode): "mac_addr": self._mac_addr, "system_id": self._system_id} - # return the relative path if the IOS image is in the images_path directory - router_info["image"] = self.manager.get_relative_image_path(self._image) + router_info["image"] = self.manager.get_relative_image_path(self._image, self.project.path) # add the slots slot_number = 0 @@ -484,7 +483,7 @@ class Router(BaseNode): :param image: path to IOS image file """ - image = self.manager.get_abs_image_path(image) + image = self.manager.get_abs_image_path(image, self.project.path) await self._hypervisor.send('vm set_ios "{name}" "{image}"'.format(name=self._name, image=image)) diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 170d3560..ad5096f4 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -75,7 +75,7 @@ class IOUVM(BaseNode): self._iou_stdout_file = "" self._started = False self._nvram_watcher = None - self._path = self.manager.get_abs_image_path(path) + self._path = self.manager.get_abs_image_path(path, project.path) self._license_check = True # IOU settings @@ -137,7 +137,7 @@ class IOUVM(BaseNode): :param path: path to the IOU image executable """ - self._path = self.manager.get_abs_image_path(path) + self._path = self.manager.get_abs_image_path(path, self.project.path) log.info('IOU "{name}" [{id}]: IOU image updated to "{path}"'.format(name=self._name, id=self._id, path=self._path)) @property @@ -232,8 +232,7 @@ class IOUVM(BaseNode): "command_line": self.command_line, "application_id": self.application_id} - # return the relative path if the IOU image is in the images_path directory - iou_vm_info["path"] = self.manager.get_relative_image_path(self.path) + iou_vm_info["path"] = self.manager.get_relative_image_path(self.path, self.project.path) return iou_vm_info @property diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index 081df9e0..fe122230 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -212,7 +212,7 @@ class QemuVM(BaseNode): :param value: New disk value """ - value = self.manager.get_abs_image_path(value) + value = self.manager.get_abs_image_path(value, self.project.path) if not self.linked_clone: for node in self.manager.nodes: if node != self and getattr(node, variable) == value: @@ -412,7 +412,8 @@ class QemuVM(BaseNode): :param cdrom_image: QEMU cdrom image path """ - self._cdrom_image = self.manager.get_abs_image_path(cdrom_image) + + self._cdrom_image = self.manager.get_abs_image_path(cdrom_image, self.project.path) log.info('QEMU VM "{name}" [{id}] has set the QEMU cdrom image path to {cdrom_image}'.format(name=self._name, id=self._id, cdrom_image=self._cdrom_image)) @@ -434,7 +435,8 @@ class QemuVM(BaseNode): :param bios_image: QEMU bios image path """ - self._bios_image = self.manager.get_abs_image_path(bios_image) + + self._bios_image = self.manager.get_abs_image_path(bios_image, self.project.path) log.info('QEMU VM "{name}" [{id}] has set the QEMU bios image path to {bios_image}'.format(name=self._name, id=self._id, bios_image=self._bios_image)) @@ -739,7 +741,7 @@ class QemuVM(BaseNode): :param initrd: QEMU initrd path """ - initrd = self.manager.get_abs_image_path(initrd) + initrd = self.manager.get_abs_image_path(initrd, self.project.path) log.info('QEMU VM "{name}" [{id}] has set the QEMU initrd path to {initrd}'.format(name=self._name, id=self._id, @@ -766,7 +768,7 @@ class QemuVM(BaseNode): :param kernel_image: QEMU kernel image path """ - kernel_image = self.manager.get_abs_image_path(kernel_image) + kernel_image = self.manager.get_abs_image_path(kernel_image, self.project.path) log.info('QEMU VM "{name}" [{id}] has set the QEMU kernel image path to {kernel_image}'.format(name=self._name, id=self._id, kernel_image=kernel_image)) @@ -1938,22 +1940,20 @@ class QemuVM(BaseNode): answer[field] = getattr(self, field) except AttributeError: pass - answer["hda_disk_image"] = self.manager.get_relative_image_path(self._hda_disk_image) + answer["hda_disk_image"] = self.manager.get_relative_image_path(self._hda_disk_image, self.project.path) answer["hda_disk_image_md5sum"] = md5sum(self._hda_disk_image) - answer["hdb_disk_image"] = self.manager.get_relative_image_path(self._hdb_disk_image) + answer["hdb_disk_image"] = self.manager.get_relative_image_path(self._hdb_disk_image, self.project.path) answer["hdb_disk_image_md5sum"] = md5sum(self._hdb_disk_image) - answer["hdc_disk_image"] = self.manager.get_relative_image_path(self._hdc_disk_image) + answer["hdc_disk_image"] = self.manager.get_relative_image_path(self._hdc_disk_image, self.project.path) answer["hdc_disk_image_md5sum"] = md5sum(self._hdc_disk_image) - answer["hdd_disk_image"] = self.manager.get_relative_image_path(self._hdd_disk_image) + answer["hdd_disk_image"] = self.manager.get_relative_image_path(self._hdd_disk_image, self.project.path) answer["hdd_disk_image_md5sum"] = md5sum(self._hdd_disk_image) - answer["cdrom_image"] = self.manager.get_relative_image_path(self._cdrom_image) + answer["cdrom_image"] = self.manager.get_relative_image_path(self._cdrom_image, self.project.path) answer["cdrom_image_md5sum"] = md5sum(self._cdrom_image) - answer["bios_image"] = self.manager.get_relative_image_path(self._bios_image) + answer["bios_image"] = self.manager.get_relative_image_path(self._bios_image, self.project.path) answer["bios_image_md5sum"] = md5sum(self._bios_image) - answer["initrd"] = self.manager.get_relative_image_path(self._initrd) + answer["initrd"] = self.manager.get_relative_image_path(self._initrd, self.project.path) answer["initrd_md5sum"] = md5sum(self._initrd) - - answer["kernel_image"] = self.manager.get_relative_image_path(self._kernel_image) + answer["kernel_image"] = self.manager.get_relative_image_path(self._kernel_image, self.project.path) answer["kernel_image_md5sum"] = md5sum(self._kernel_image) - return answer diff --git a/tests/handlers/api/compute/test_qemu.py b/tests/handlers/api/compute/test_qemu.py index 7c63edcd..a7636ee1 100644 --- a/tests/handlers/api/compute/test_qemu.py +++ b/tests/handlers/api/compute/test_qemu.py @@ -101,6 +101,17 @@ def test_qemu_create_with_params(http_compute, project, base_params, fake_qemu_v assert response.json["hda_disk_image_md5sum"] == "c4ca4238a0b923820dcc509a6f75849b" +def test_qemu_create_with_project_file(http_compute, project, base_params, fake_qemu_vm): + response = http_compute.post("/projects/{project_id}/files/hello.img".format(project_id=project.id), body="world", raw=True) + assert response.status == 200 + params = base_params + params["hda_disk_image"] = "hello.img" + response = http_compute.post("/projects/{project_id}/qemu/nodes".format(project_id=project.id), params, example=True) + assert response.status == 201 + assert response.json["hda_disk_image"] == "hello.img" + assert response.json["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7" + + def test_qemu_get(http_compute, project, vm): response = http_compute.get("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), example=True) assert response.status == 200 From 372748773027cfbca2e159c79a0a2f0d36a9be4e Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 19 Nov 2018 16:09:39 +0700 Subject: [PATCH 2/2] Fix test on Windows. --- tests/handlers/api/compute/test_qemu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/handlers/api/compute/test_qemu.py b/tests/handlers/api/compute/test_qemu.py index a7636ee1..352db57a 100644 --- a/tests/handlers/api/compute/test_qemu.py +++ b/tests/handlers/api/compute/test_qemu.py @@ -101,6 +101,7 @@ def test_qemu_create_with_params(http_compute, project, base_params, fake_qemu_v assert response.json["hda_disk_image_md5sum"] == "c4ca4238a0b923820dcc509a6f75849b" +@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") def test_qemu_create_with_project_file(http_compute, project, base_params, fake_qemu_vm): response = http_compute.post("/projects/{project_id}/files/hello.img".format(project_id=project.id), body="world", raw=True) assert response.status == 200