mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-18 14:28:08 +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:
commit
ca6a0708a8
@ -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)
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user