1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-18 22:38:07 +00:00

Merge pull request #1462 from GNS3/use-files-as-disk-images

Allow virtual machines to use files in project directory as disk images.
This commit is contained in:
Jeremy Grossmann 2018-11-19 16:41:36 +07:00 committed by GitHub
commit ca6a0708a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 31 deletions

View File

@ -469,12 +469,14 @@ class BaseManager:
except PermissionError: except PermissionError:
raise aiohttp.web.HTTPForbidden() 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 Get the absolute path of an image
:param path: file path :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: if not path:
@ -483,6 +485,9 @@ class BaseManager:
server_config = self.config.get_section_config("Server") server_config = self.config.get_section_config("Server")
img_directory = self.get_images_directory() 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 # Windows path should not be send to a unix server
if not sys.platform.startswith("win"): 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)) 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): 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) path = self._recursive_search_file_in_directory(directory, orig_path)
if path: if path:
return force_unix_path(path) return force_unix_path(path)
@ -502,15 +507,16 @@ class BaseManager:
return path return path
raise ImageMissingError(orig_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: if server_config.getboolean("local", False) is True:
path = force_unix_path(path) path = force_unix_path(path)
if os.path.exists(path): if os.path.exists(path):
return path return path
raise ImageMissingError(orig_path) raise ImageMissingError(orig_path)
# Check to see if path is an absolute path to a valid directory
path = force_unix_path(path) 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.commonprefix([directory, path]) == directory:
if os.path.exists(path): if os.path.exists(path):
return path return path
@ -534,23 +540,29 @@ class BaseManager:
return path return path
return None 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 Get a path relative to images directory path
or an abspath if the path is not located inside or an abspath if the path is not located inside
image directory image directory
:param path: file path :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: if not path:
return "" 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() 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: if os.path.commonprefix([directory, path]) == directory:
relpath = os.path.relpath(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) # We don't allow to recurse search from the top image directory just for image type directory (compatibility with old releases)

View File

@ -169,8 +169,7 @@ class Router(BaseNode):
"mac_addr": self._mac_addr, "mac_addr": self._mac_addr,
"system_id": self._system_id} "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, self.project.path)
router_info["image"] = self.manager.get_relative_image_path(self._image)
# add the slots # add the slots
slot_number = 0 slot_number = 0
@ -484,7 +483,7 @@ class Router(BaseNode):
:param image: path to IOS image file :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)) await self._hypervisor.send('vm set_ios "{name}" "{image}"'.format(name=self._name, image=image))

View File

@ -75,7 +75,7 @@ class IOUVM(BaseNode):
self._iou_stdout_file = "" self._iou_stdout_file = ""
self._started = False self._started = False
self._nvram_watcher = None 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 self._license_check = True
# IOU settings # IOU settings
@ -137,7 +137,7 @@ class IOUVM(BaseNode):
:param path: path to the IOU image executable :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)) log.info('IOU "{name}" [{id}]: IOU image updated to "{path}"'.format(name=self._name, id=self._id, path=self._path))
@property @property
@ -232,8 +232,7 @@ class IOUVM(BaseNode):
"command_line": self.command_line, "command_line": self.command_line,
"application_id": self.application_id} "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, self.project.path)
iou_vm_info["path"] = self.manager.get_relative_image_path(self.path)
return iou_vm_info return iou_vm_info
@property @property

View File

@ -212,7 +212,7 @@ class QemuVM(BaseNode):
:param value: New disk value :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: if not self.linked_clone:
for node in self.manager.nodes: for node in self.manager.nodes:
if node != self and getattr(node, variable) == value: if node != self and getattr(node, variable) == value:
@ -412,7 +412,8 @@ class QemuVM(BaseNode):
:param cdrom_image: QEMU cdrom image path :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, log.info('QEMU VM "{name}" [{id}] has set the QEMU cdrom image path to {cdrom_image}'.format(name=self._name,
id=self._id, id=self._id,
cdrom_image=self._cdrom_image)) cdrom_image=self._cdrom_image))
@ -434,7 +435,8 @@ class QemuVM(BaseNode):
:param bios_image: QEMU bios image path :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, log.info('QEMU VM "{name}" [{id}] has set the QEMU bios image path to {bios_image}'.format(name=self._name,
id=self._id, id=self._id,
bios_image=self._bios_image)) bios_image=self._bios_image))
@ -739,7 +741,7 @@ class QemuVM(BaseNode):
:param initrd: QEMU initrd path :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, log.info('QEMU VM "{name}" [{id}] has set the QEMU initrd path to {initrd}'.format(name=self._name,
id=self._id, id=self._id,
@ -766,7 +768,7 @@ class QemuVM(BaseNode):
:param kernel_image: QEMU kernel image path :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, log.info('QEMU VM "{name}" [{id}] has set the QEMU kernel image path to {kernel_image}'.format(name=self._name,
id=self._id, id=self._id,
kernel_image=kernel_image)) kernel_image=kernel_image))
@ -1938,22 +1940,20 @@ class QemuVM(BaseNode):
answer[field] = getattr(self, field) answer[field] = getattr(self, field)
except AttributeError: except AttributeError:
pass 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["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["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["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["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["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["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["initrd_md5sum"] = md5sum(self._initrd)
answer["kernel_image"] = self.manager.get_relative_image_path(self._kernel_image, self.project.path)
answer["kernel_image"] = self.manager.get_relative_image_path(self._kernel_image)
answer["kernel_image_md5sum"] = md5sum(self._kernel_image) answer["kernel_image_md5sum"] = md5sum(self._kernel_image)
return answer return answer

View File

@ -101,6 +101,18 @@ def test_qemu_create_with_params(http_compute, project, base_params, fake_qemu_v
assert response.json["hda_disk_image_md5sum"] == "c4ca4238a0b923820dcc509a6f75849b" 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
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): 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) 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 assert response.status == 200