mirror of
https://github.com/GNS3/gns3-server.git
synced 2025-06-19 15:53:47 +00:00
Allow images to be stored in subdirs and used by templates.
This commit is contained in:
@ -23,6 +23,7 @@ import logging
|
||||
import urllib.parse
|
||||
|
||||
from fastapi import APIRouter, Request, Response, Depends, status
|
||||
from sqlalchemy.orm.exc import MultipleResultsFound
|
||||
from typing import List
|
||||
from gns3server import schemas
|
||||
|
||||
@ -53,9 +54,9 @@ async def get_images(
|
||||
return await images_repo.get_images()
|
||||
|
||||
|
||||
@router.post("/upload/{image_name}", response_model=schemas.Image, status_code=status.HTTP_201_CREATED)
|
||||
@router.post("/upload/{image_path:path}", response_model=schemas.Image, status_code=status.HTTP_201_CREATED)
|
||||
async def upload_image(
|
||||
image_name: str,
|
||||
image_path: str,
|
||||
request: Request,
|
||||
image_type: schemas.ImageType = schemas.ImageType.qemu,
|
||||
images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)),
|
||||
@ -64,19 +65,20 @@ async def upload_image(
|
||||
Upload an image.
|
||||
"""
|
||||
|
||||
image_name = urllib.parse.unquote(image_name)
|
||||
image_path = urllib.parse.unquote(image_path)
|
||||
image_dir, image_name = os.path.split(image_path)
|
||||
directory = default_images_directory(image_type)
|
||||
path = os.path.abspath(os.path.join(directory, image_name))
|
||||
if os.path.commonprefix([directory, path]) != directory:
|
||||
raise ControllerForbiddenError(f"Could not write image: {image_name}, '{path}' is forbidden")
|
||||
full_path = os.path.abspath(os.path.join(directory, image_dir, image_name))
|
||||
if os.path.commonprefix([directory, full_path]) != directory:
|
||||
raise ControllerForbiddenError(f"Could not write image, '{image_path}' is forbidden")
|
||||
|
||||
if await images_repo.get_image(image_name):
|
||||
raise ControllerBadRequestError(f"Image '{image_name}' already exists")
|
||||
if await images_repo.get_image(image_path):
|
||||
raise ControllerBadRequestError(f"Image '{image_path}' already exists")
|
||||
|
||||
try:
|
||||
image = await write_image(image_name, image_type, path, request.stream(), images_repo)
|
||||
image = await write_image(image_name, image_type, full_path, request.stream(), images_repo)
|
||||
except (OSError, InvalidImageError) as e:
|
||||
raise ControllerError(f"Could not save {image_type} image '{image_name}': {e}")
|
||||
raise ControllerError(f"Could not save {image_type} image '{image_path}': {e}")
|
||||
|
||||
# TODO: automatically create template based on image checksum
|
||||
#from gns3server.controller import Controller
|
||||
@ -86,45 +88,53 @@ async def upload_image(
|
||||
return image
|
||||
|
||||
|
||||
@router.get("/{image_name}", response_model=schemas.Image)
|
||||
@router.get("/{image_path:path}", response_model=schemas.Image)
|
||||
async def get_image(
|
||||
image_name: str,
|
||||
image_path: str,
|
||||
images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)),
|
||||
) -> schemas.Image:
|
||||
"""
|
||||
Return an image.
|
||||
"""
|
||||
|
||||
image = await images_repo.get_image(image_name)
|
||||
image_path = urllib.parse.unquote(image_path)
|
||||
image = await images_repo.get_image(image_path)
|
||||
if not image:
|
||||
raise ControllerNotFoundError(f"Image '{image_name}' not found")
|
||||
raise ControllerNotFoundError(f"Image '{image_path}' not found")
|
||||
return image
|
||||
|
||||
|
||||
@router.delete("/{image_name}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
@router.delete("/{image_path:path}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_image(
|
||||
image_name: str,
|
||||
image_path: str,
|
||||
images_repo: ImagesRepository = Depends(get_repository(ImagesRepository)),
|
||||
) -> None:
|
||||
"""
|
||||
Delete an image.
|
||||
"""
|
||||
|
||||
image = await images_repo.get_image(image_name)
|
||||
image_path = urllib.parse.unquote(image_path)
|
||||
|
||||
try:
|
||||
image = await images_repo.get_image(image_path)
|
||||
except MultipleResultsFound:
|
||||
raise ControllerBadRequestError(f"Image '{image_path}' matches multiple images. "
|
||||
f"Please include the relative path of the image")
|
||||
|
||||
if not image:
|
||||
raise ControllerNotFoundError(f"Image '{image_name}' not found")
|
||||
raise ControllerNotFoundError(f"Image '{image_path}' not found")
|
||||
|
||||
if await images_repo.get_image_templates(image.id):
|
||||
raise ControllerError(f"Image '{image_name}' is used by one or more templates")
|
||||
raise ControllerError(f"Image '{image_path}' is used by one or more templates")
|
||||
|
||||
try:
|
||||
os.remove(image.path)
|
||||
except OSError:
|
||||
log.warning(f"Could not delete image file {image.path}")
|
||||
|
||||
success = await images_repo.delete_image(image_name)
|
||||
success = await images_repo.delete_image(image_path)
|
||||
if not success:
|
||||
raise ControllerError(f"Image '{image_name}' could not be deleted")
|
||||
raise ControllerError(f"Image '{image_path}' could not be deleted")
|
||||
|
||||
|
||||
@router.post("/prune", status_code=status.HTTP_204_NO_CONTENT)
|
||||
|
Reference in New Issue
Block a user