Refactor tests and start work on database integration.

This commit is contained in:
grossmj 2020-12-02 18:39:08 +10:30
parent ae55c0ec9c
commit bf7cf862af
69 changed files with 2955 additions and 1877 deletions

View File

@ -1,34 +1,22 @@
FROM ubuntu:20.04 FROM python:3.6-alpine3.11
ENV DEBIAN_FRONTEND noninteractive WORKDIR /gns3server
# Set the locale
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONBUFFERED 1
RUN apt-get update && apt-get install -y software-properties-common COPY ./requirements.txt /gns3server/requirements.txt
RUN add-apt-repository ppa:gns3/ppa
RUN apt-get update && apt-get install -y \
git \
locales \
python3-pip \
python3-dev \
qemu-system-x86 \
qemu-kvm \
libvirt-daemon-system \
x11vnc
RUN locale-gen en_US.UTF-8 RUN set -eux \
&& apk add --no-cache --virtual .build-deps build-base \
gcc libc-dev musl-dev linux-headers python3-dev \
vpcs qemu libvirt ubridge \
&& pip install --upgrade pip setuptools wheel \
&& pip install -r /gns3server/requirements.txt \
&& rm -rf /root/.cache/pip
# Install uninstall to install dependencies COPY . /gns3server
RUN apt-get install -y vpcs ubridge RUN python3 setup.py install
ADD . /server
WORKDIR /server
RUN pip3 install -r /server/requirements.txt
EXPOSE 3080
CMD python3 -m gns3server

View File

@ -4,5 +4,6 @@ pytest==6.1.2
flake8==3.8.4 flake8==3.8.4
pytest-timeout==1.4.2 pytest-timeout==1.4.2
pytest-asyncio==0.14.0 pytest-asyncio==0.14.0
asgi-lifespan==1.0.1
requests==2.24.0 requests==2.24.0
httpx==0.16.1 httpx==0.16.1

15
docker-compose.yml Normal file
View File

@ -0,0 +1,15 @@
version: '3.7'
services:
gns3server:
privileged: true
build:
context: .
dockerfile: Dockerfile
volumes:
- ./gns3server:/server/
- /var/run/docker.sock:/var/run/docker.sock
command: python3 -m gns3server --local --port 3080
ports:
- 3080:3080
- 5000-5100:5000-5100

View File

@ -35,7 +35,7 @@ router = APIRouter()
@router.get("/capabilities", @router.get("/capabilities",
response_model=schemas.Capabilities response_model=schemas.Capabilities
) )
def get_compute_capabilities(): def get_capabilities():
node_types = [] node_types = []
for module in MODULES: for module in MODULES:

View File

@ -104,7 +104,7 @@ def update_cloud(node_data: schemas.CloudUpdate, node: Cloud = Depends(dep_node)
@router.delete("/{node_id}", @router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_node(node: Cloud = Depends(dep_node)): async def delete_cloud(node: Cloud = Depends(dep_node)):
""" """
Delete a cloud node. Delete a cloud node.
""" """
@ -151,10 +151,10 @@ async def suspend_cloud(node: Cloud = Depends(dep_node)):
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses=responses) responses=responses)
async def create_nio(adapter_number: int, async def create_cloud_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
node: Cloud = Depends(dep_node)): node: Cloud = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -169,10 +169,10 @@ async def create_nio(adapter_number: int,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses=responses) responses=responses)
async def update_nio(adapter_number: int, async def update_cloud_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
node: Cloud = Depends(dep_node)): node: Cloud = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) to the node. Update a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -188,7 +188,7 @@ async def update_nio(adapter_number: int,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)): async def delete_cloud_nio(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
""" """
Remove a NIO (Network Input/Output) from the node. Remove a NIO (Network Input/Output) from the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -199,10 +199,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: Cloud = Depend
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_cloud_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: Cloud = Depends(dep_node)): node: Cloud = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -216,7 +216,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)): async def stop_cloud_capture(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.

View File

@ -78,7 +78,7 @@ def network_ports() -> dict:
@router.get("/version") @router.get("/version")
def version() -> dict: def compute_version() -> dict:
""" """
Retrieve the server version number. Retrieve the server version number.
""" """
@ -89,7 +89,7 @@ def version() -> dict:
@router.get("/statistics") @router.get("/statistics")
def statistics() -> dict: def compute_statistics() -> dict:
""" """
Retrieve the server version number. Retrieve the server version number.
""" """
@ -124,19 +124,19 @@ def statistics() -> dict:
@router.get("/qemu/binaries") @router.get("/qemu/binaries")
async def get_binaries(archs: Optional[List[str]] = Body(None, embed=True)): async def get_qemu_binaries(archs: Optional[List[str]] = Body(None, embed=True)):
return await Qemu.binary_list(archs) return await Qemu.binary_list(archs)
@router.get("/qemu/img-binaries") @router.get("/qemu/img-binaries")
async def get_img_binaries(): async def get_image_binaries():
return await Qemu.img_binary_list() return await Qemu.img_binary_list()
@router.get("/qemu/capabilities") @router.get("/qemu/capabilities")
async def get_capabilities() -> dict: async def get_qemu_capabilities() -> dict:
capabilities = {"kvm": []} capabilities = {"kvm": []}
kvms = await Qemu.get_kvm_archs() kvms = await Qemu.get_kvm_archs()
if kvms: if kvms:
@ -147,7 +147,7 @@ async def get_capabilities() -> dict:
@router.post("/qemu/img", @router.post("/qemu/img",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={403: {"model": schemas.ErrorMessage, "description": "Forbidden to create Qemu image"}}) responses={403: {"model": schemas.ErrorMessage, "description": "Forbidden to create Qemu image"}})
async def create_img(image_data: schemas.QemuImageCreate): async def create_qemu_image(image_data: schemas.QemuImageCreate):
""" """
Create a Qemu image. Create a Qemu image.
""" """
@ -163,7 +163,7 @@ async def create_img(image_data: schemas.QemuImageCreate):
@router.put("/qemu/img", @router.put("/qemu/img",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={403: {"model": schemas.ErrorMessage, "description": "Forbidden to update Qemu image"}}) responses={403: {"model": schemas.ErrorMessage, "description": "Forbidden to update Qemu image"}})
async def update_img(image_data: schemas.QemuImageUpdate): async def update_qemu_image(image_data: schemas.QemuImageUpdate):
""" """
Update a Qemu image. Update a Qemu image.
""" """

View File

@ -98,7 +98,7 @@ def get_docker_node(node: DockerVM = Depends(dep_node)):
@router.put("/{node_id}", @router.put("/{node_id}",
response_model=schemas.Docker, response_model=schemas.Docker,
responses=responses) responses=responses)
async def update_docker(node_data: schemas.DockerUpdate, node: DockerVM = Depends(dep_node)): async def update_docker_node(node_data: schemas.DockerUpdate, node: DockerVM = Depends(dep_node)):
""" """
Update a Docker node. Update a Docker node.
""" """
@ -217,10 +217,10 @@ async def duplicate_docker_node(destination_node_id: UUID = Body(..., embed=True
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def create_nio(adapter_number: int, async def create_docker_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: schemas.UDPNIO, nio_data: schemas.UDPNIO,
node: DockerVM = Depends(dep_node)): node: DockerVM = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
The port number on the Docker node is always 0. The port number on the Docker node is always 0.
@ -235,9 +235,9 @@ async def create_nio(adapter_number: int,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def update_nio(adapter_number: int, async def update_docker_node_nio(adapter_number: int,
port_number: int, nio_data: schemas.UDPNIO, port_number: int, nio_data: schemas.UDPNIO,
node: DockerVM = Depends(dep_node)): node: DockerVM = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) on the node. Update a NIO (Network Input/Output) on the node.
The port number on the Docker node is always 0. The port number on the Docker node is always 0.
@ -253,7 +253,7 @@ async def update_nio(adapter_number: int,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)): async def delete_docker_node_nio(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
""" """
Delete a NIO (Network Input/Output) from the node. Delete a NIO (Network Input/Output) from the node.
The port number on the Docker node is always 0. The port number on the Docker node is always 0.
@ -264,10 +264,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: DockerVM = Dep
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_docker_node_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: DockerVM = Depends(dep_node)): node: DockerVM = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
The port number on the Docker node is always 0. The port number on the Docker node is always 0.
@ -281,7 +281,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)): async def stop_docker_node_capture(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The port number on the Docker node is always 0. The port number on the Docker node is always 0.

View File

@ -159,7 +159,7 @@ async def start_iou_node(start_data: schemas.IOUStart, node: IOUVM = Depends(dep
@router.post("/{node_id}/stop", @router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop(node: IOUVM = Depends(dep_node)): async def stop_iou_node(node: IOUVM = Depends(dep_node)):
""" """
Stop an IOU node. Stop an IOU node.
""" """
@ -194,10 +194,10 @@ async def reload_iou_node(node: IOUVM = Depends(dep_node)):
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses=responses) responses=responses)
async def create_nio(adapter_number: int, async def create_iou_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
node: IOUVM = Depends(dep_node)): node: IOUVM = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
""" """
@ -211,10 +211,10 @@ async def create_nio(adapter_number: int,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses=responses) responses=responses)
async def update_nio(adapter_number: int, async def update_iou_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
node: IOUVM = Depends(dep_node)): node: IOUVM = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) on the node. Update a NIO (Network Input/Output) on the node.
""" """
@ -229,7 +229,7 @@ async def update_nio(adapter_number: int,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)): async def delete_iou_node_nio(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
""" """
Delete a NIO (Network Input/Output) from the node. Delete a NIO (Network Input/Output) from the node.
""" """
@ -239,10 +239,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: IOUVM = Depend
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_iou_node_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: IOUVM = Depends(dep_node)): node: IOUVM = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
""" """
@ -255,7 +255,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)): async def stop_iou_node_capture(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
""" """

View File

@ -52,7 +52,7 @@ def dep_node(project_id: UUID, node_id: UUID):
response_model=schemas.NAT, response_model=schemas.NAT,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
responses={409: {"model": schemas.ErrorMessage, "description": "Could not create NAT node"}}) responses={409: {"model": schemas.ErrorMessage, "description": "Could not create NAT node"}})
async def create_nat(project_id: UUID, node_data: schemas.NATCreate): async def create_nat_node(project_id: UUID, node_data: schemas.NATCreate):
""" """
Create a new NAT node. Create a new NAT node.
""" """
@ -72,7 +72,7 @@ async def create_nat(project_id: UUID, node_data: schemas.NATCreate):
@router.get("/{node_id}", @router.get("/{node_id}",
response_model=schemas.NAT, response_model=schemas.NAT,
responses=responses) responses=responses)
def get_nat(node: Nat = Depends(dep_node)): def get_nat_node(node: Nat = Depends(dep_node)):
""" """
Return a NAT node. Return a NAT node.
""" """
@ -83,7 +83,7 @@ def get_nat(node: Nat = Depends(dep_node)):
@router.put("/{node_id}", @router.put("/{node_id}",
response_model=schemas.NAT, response_model=schemas.NAT,
responses=responses) responses=responses)
def update_nat(node_data: schemas.NATUpdate, node: Nat = Depends(dep_node)): def update_nat_node(node_data: schemas.NATUpdate, node: Nat = Depends(dep_node)):
""" """
Update a NAT node. Update a NAT node.
""" """
@ -99,7 +99,7 @@ def update_nat(node_data: schemas.NATUpdate, node: Nat = Depends(dep_node)):
@router.delete("/{node_id}", @router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nat(node: Nat = Depends(dep_node)): async def delete_nat_node(node: Nat = Depends(dep_node)):
""" """
Delete a cloud node. Delete a cloud node.
""" """
@ -110,7 +110,7 @@ async def delete_nat(node: Nat = Depends(dep_node)):
@router.post("/{node_id}/start", @router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def start_nat(node: Nat = Depends(dep_node)): async def start_nat_node(node: Nat = Depends(dep_node)):
""" """
Start a NAT node. Start a NAT node.
""" """
@ -121,7 +121,7 @@ async def start_nat(node: Nat = Depends(dep_node)):
@router.post("/{node_id}/stop", @router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_nat(node: Nat = Depends(dep_node)): async def stop_nat_node(node: Nat = Depends(dep_node)):
""" """
Stop a NAT node. Stop a NAT node.
This endpoint results in no action since cloud nodes cannot be stopped. This endpoint results in no action since cloud nodes cannot be stopped.
@ -133,7 +133,7 @@ async def stop_nat(node: Nat = Depends(dep_node)):
@router.post("/{node_id}/suspend", @router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def suspend_nat(node: Nat = Depends(dep_node)): async def suspend_nat_node(node: Nat = Depends(dep_node)):
""" """
Suspend a NAT node. Suspend a NAT node.
This endpoint results in no action since NAT nodes cannot be suspended. This endpoint results in no action since NAT nodes cannot be suspended.
@ -146,10 +146,10 @@ async def suspend_nat(node: Nat = Depends(dep_node)):
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses=responses) responses=responses)
async def create_nio(adapter_number: int, async def create_nat_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
node: Nat = Depends(dep_node)): node: Nat = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -164,10 +164,10 @@ async def create_nio(adapter_number: int,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses=responses) responses=responses)
async def update_nio(adapter_number: int, async def update_nat_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO], nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
node: Nat = Depends(dep_node)): node: Nat = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) to the node. Update a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -183,7 +183,7 @@ async def update_nio(adapter_number: int,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)): async def delete_nat_node_nio(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
""" """
Remove a NIO (Network Input/Output) from the node. Remove a NIO (Network Input/Output) from the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -194,10 +194,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: Nat = Depends(
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_nat_node_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: Nat = Depends(dep_node)): node: Nat = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.
@ -211,7 +211,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)): async def stop_nat_node_capture(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The adapter number on the cloud is always 0. The adapter number on the cloud is always 0.

View File

@ -52,7 +52,7 @@ def dep_project(project_id: UUID):
@router.get("/projects", response_model=List[schemas.Project]) @router.get("/projects", response_model=List[schemas.Project])
def get_projects(): def get_compute_projects():
""" """
Get all projects opened on the compute. Get all projects opened on the compute.
""" """
@ -64,7 +64,7 @@ def get_projects():
@router.post("/projects", @router.post("/projects",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Project) response_model=schemas.Project)
def create_project(project_data: schemas.ProjectCreate): def create_compute_project(project_data: schemas.ProjectCreate):
""" """
Create a new project on the compute. Create a new project on the compute.
""" """
@ -80,7 +80,7 @@ def create_project(project_data: schemas.ProjectCreate):
@router.put("/projects/{project_id}", @router.put("/projects/{project_id}",
response_model=schemas.Project) response_model=schemas.Project)
async def update_project(project_data: schemas.ProjectUpdate, project: Project = Depends(dep_project)): async def update_compute_project(project_data: schemas.ProjectUpdate, project: Project = Depends(dep_project)):
""" """
Update project on the compute. Update project on the compute.
""" """
@ -91,7 +91,7 @@ async def update_project(project_data: schemas.ProjectUpdate, project: Project =
@router.get("/projects/{project_id}", @router.get("/projects/{project_id}",
response_model=schemas.Project) response_model=schemas.Project)
def get_project(project: Project = Depends(dep_project)): def get_compute_project(project: Project = Depends(dep_project)):
""" """
Return a project from the compute. Return a project from the compute.
""" """
@ -101,7 +101,7 @@ def get_project(project: Project = Depends(dep_project)):
@router.post("/projects/{project_id}/close", @router.post("/projects/{project_id}/close",
status_code=status.HTTP_204_NO_CONTENT) status_code=status.HTTP_204_NO_CONTENT)
async def close_project(project: Project = Depends(dep_project)): async def close_compute_project(project: Project = Depends(dep_project)):
""" """
Close a project on the compute. Close a project on the compute.
""" """
@ -120,7 +120,7 @@ async def close_project(project: Project = Depends(dep_project)):
@router.delete("/projects/{project_id}", @router.delete("/projects/{project_id}",
status_code=status.HTTP_204_NO_CONTENT) status_code=status.HTTP_204_NO_CONTENT)
async def delete_project(project: Project = Depends(dep_project)): async def delete_compute_project(project: Project = Depends(dep_project)):
""" """
Delete project from the compute. Delete project from the compute.
""" """
@ -184,7 +184,7 @@ async def delete_project(project: Project = Depends(dep_project)):
@router.get("/projects/{project_id}/files", @router.get("/projects/{project_id}/files",
response_model=List[schemas.ProjectFile]) response_model=List[schemas.ProjectFile])
async def get_project_files(project: Project = Depends(dep_project)): async def get_compute_project_files(project: Project = Depends(dep_project)):
""" """
Return files belonging to a project. Return files belonging to a project.
""" """
@ -193,7 +193,7 @@ async def get_project_files(project: Project = Depends(dep_project)):
@router.get("/projects/{project_id}/files/{file_path:path}") @router.get("/projects/{project_id}/files/{file_path:path}")
async def get_file(file_path: str, project: Project = Depends(dep_project)): async def get_compute_project_file(file_path: str, project: Project = Depends(dep_project)):
""" """
Get a file from a project. Get a file from a project.
""" """
@ -213,7 +213,7 @@ async def get_file(file_path: str, project: Project = Depends(dep_project)):
@router.post("/projects/{project_id}/files/{file_path:path}", @router.post("/projects/{project_id}/files/{file_path:path}",
status_code=status.HTTP_204_NO_CONTENT) status_code=status.HTTP_204_NO_CONTENT)
async def write_file(file_path: str, request: Request, project: Project = Depends(dep_project)): async def write_compute_project_file(file_path: str, request: Request, project: Project = Depends(dep_project)):
path = os.path.normpath(file_path) path = os.path.normpath(file_path)

View File

@ -210,7 +210,10 @@ async def resume_qemu_node(node: QemuVM = Depends(dep_node)):
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: QemuVM = Depends(dep_node)): async def create_qemu_node_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: QemuVM = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
The port number on the Qemu node is always 0. The port number on the Qemu node is always 0.
@ -225,7 +228,10 @@ async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UD
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: QemuVM = Depends(dep_node)): async def update_qemu_node_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: QemuVM = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) on the node. Update a NIO (Network Input/Output) on the node.
The port number on the Qemu node is always 0. The port number on the Qemu node is always 0.
@ -243,7 +249,9 @@ async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UD
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)): async def delete_qemu_node_nio(adapter_number: int,
port_number: int,
node: QemuVM = Depends(dep_node)):
""" """
Delete a NIO (Network Input/Output) from the node. Delete a NIO (Network Input/Output) from the node.
The port number on the Qemu node is always 0. The port number on the Qemu node is always 0.
@ -254,10 +262,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: QemuVM = Depen
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_qemu_node_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: QemuVM = Depends(dep_node)): node: QemuVM = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
The port number on the Qemu node is always 0. The port number on the Qemu node is always 0.
@ -271,7 +279,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)): async def stop_qemu_node_capture(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The port number on the Qemu node is always 0. The port number on the Qemu node is always 0.

View File

@ -212,10 +212,10 @@ async def reload_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def create_nio(adapter_number: int, async def create_virtualbox_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: schemas.UDPNIO, nio_data: schemas.UDPNIO,
node: VirtualBoxVM = Depends(dep_node)): node: VirtualBoxVM = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
The port number on the VirtualBox node is always 0. The port number on the VirtualBox node is always 0.
@ -230,10 +230,10 @@ async def create_nio(adapter_number: int,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def update_nio(adapter_number: int, async def update_virtualbox_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: schemas.UDPNIO, nio_data: schemas.UDPNIO,
node: VirtualBoxVM = Depends(dep_node)): node: VirtualBoxVM = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) on the node. Update a NIO (Network Input/Output) on the node.
The port number on the VirtualBox node is always 0. The port number on the VirtualBox node is always 0.
@ -251,7 +251,7 @@ async def update_nio(adapter_number: int,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)): async def delete_virtualbox_node_nio(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
""" """
Delete a NIO (Network Input/Output) from the node. Delete a NIO (Network Input/Output) from the node.
The port number on the VirtualBox node is always 0. The port number on the VirtualBox node is always 0.
@ -262,10 +262,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: VirtualBoxVM =
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_virtualbox_node_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: VirtualBoxVM = Depends(dep_node)): node: VirtualBoxVM = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
The port number on the VirtualBox node is always 0. The port number on the VirtualBox node is always 0.
@ -279,7 +279,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)): async def stop_virtualbox_node_capture(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The port number on the VirtualBox node is always 0. The port number on the VirtualBox node is always 0.

View File

@ -180,10 +180,10 @@ async def reload_vmware_node(node: VMwareVM = Depends(dep_node)):
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def create_nio(adapter_number: int, async def create_vmware_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: schemas.UDPNIO, nio_data: schemas.UDPNIO,
node: VMwareVM = Depends(dep_node)): node: VMwareVM = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
The port number on the VMware node is always 0. The port number on the VMware node is always 0.
@ -198,9 +198,9 @@ async def create_nio(adapter_number: int,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def update_nio(adapter_number: int, async def update_vmware_node_nio(adapter_number: int,
port_number: int, port_number: int,
nio_data: schemas.UDPNIO, node: VMwareVM = Depends(dep_node)): nio_data: schemas.UDPNIO, node: VMwareVM = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) on the node. Update a NIO (Network Input/Output) on the node.
The port number on the VMware node is always 0. The port number on the VMware node is always 0.
@ -216,7 +216,7 @@ async def update_nio(adapter_number: int,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)): async def delete_vmware_node_nio(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
""" """
Delete a NIO (Network Input/Output) from the node. Delete a NIO (Network Input/Output) from the node.
The port number on the VMware node is always 0. The port number on the VMware node is always 0.
@ -227,10 +227,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: VMwareVM = Dep
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_vmware_node_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: VMwareVM = Depends(dep_node)): node: VMwareVM = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
The port number on the VMware node is always 0. The port number on the VMware node is always 0.
@ -244,7 +244,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)): async def stop_vmware_node_capture(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The port number on the VMware node is always 0. The port number on the VMware node is always 0.

View File

@ -168,7 +168,10 @@ async def reload_vpcs_node(node: VPCSVM = Depends(dep_node)):
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: VPCSVM = Depends(dep_node)): async def create_vpcs_node_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: VPCSVM = Depends(dep_node)):
""" """
Add a NIO (Network Input/Output) to the node. Add a NIO (Network Input/Output) to the node.
The adapter number on the VPCS node is always 0. The adapter number on the VPCS node is always 0.
@ -183,7 +186,10 @@ async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UD
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses=responses) responses=responses)
async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: VPCSVM = Depends(dep_node)): async def update_vpcs_node_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: VPCSVM = Depends(dep_node)):
""" """
Update a NIO (Network Input/Output) on the node. Update a NIO (Network Input/Output) on the node.
The adapter number on the VPCS node is always 0. The adapter number on the VPCS node is always 0.
@ -199,7 +205,9 @@ async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UD
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)): async def delete_vpcs_node_nio(adapter_number: int,
port_number: int,
node: VPCSVM = Depends(dep_node)):
""" """
Delete a NIO (Network Input/Output) from the node. Delete a NIO (Network Input/Output) from the node.
The adapter number on the VPCS node is always 0. The adapter number on the VPCS node is always 0.
@ -210,10 +218,10 @@ async def delete_nio(adapter_number: int, port_number: int, node: VPCSVM = Depen
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/start",
responses=responses) responses=responses)
async def start_capture(adapter_number: int, async def start_vpcs_node_capture(adapter_number: int,
port_number: int, port_number: int,
node_capture_data: schemas.NodeCapture, node_capture_data: schemas.NodeCapture,
node: VPCSVM = Depends(dep_node)): node: VPCSVM = Depends(dep_node)):
""" """
Start a packet capture on the node. Start a packet capture on the node.
The adapter number on the VPCS node is always 0. The adapter number on the VPCS node is always 0.
@ -227,7 +235,7 @@ async def start_capture(adapter_number: int,
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/capture/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)): async def stop_vpcs_node_capture(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The adapter number on the VPCS node is always 0. The adapter number on the VPCS node is always 0.

View File

@ -28,9 +28,11 @@ from . import projects
from . import snapshots from . import snapshots
from . import symbols from . import symbols
from . import templates from . import templates
from . import users
router = APIRouter() router = APIRouter()
router.include_router(controller.router, tags=["Controller"]) router.include_router(controller.router, tags=["Controller"])
router.include_router(users.router, prefix="/users", tags=["Users"])
router.include_router(appliances.router, prefix="/appliances", tags=["Appliances"]) router.include_router(appliances.router, prefix="/appliances", tags=["Appliances"])
router.include_router(computes.router, prefix="/computes", tags=["Computes"]) router.include_router(computes.router, prefix="/computes", tags=["Computes"])
router.include_router(drawings.router, prefix="/projects/{project_id}/drawings", tags=["Drawings"]) router.include_router(drawings.router, prefix="/projects/{project_id}/drawings", tags=["Drawings"])

View File

@ -71,7 +71,7 @@ async def shutdown():
@router.get("/version", @router.get("/version",
response_model=schemas.Version) response_model=schemas.Version)
def version(): def get_version():
""" """
Return the server version number. Return the server version number.
""" """

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from gns3server import schemas
from gns3server.db.repositories.users import UsersRepository
from gns3server.services import auth_service
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/v3/users/login") # FIXME: URL prefix
async def get_user_from_token(token: str = Depends(oauth2_scheme),
user_repo: UsersRepository = Depends()) -> schemas.User:
username = auth_service.get_username_from_token(token)
user = await user_repo.get_user_by_username(username)
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"}
)
return user
async def get_current_active_user(current_user: schemas.User = Depends(get_user_from_token)) -> schemas.User:
if not current_user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not an active user",
headers={"WWW-Authenticate": "Bearer"}
)
return current_user

View File

@ -416,7 +416,7 @@ async def ws_console(websocket: WebSocket, node: Node = Depends(dep_node)):
@router.post("/console/reset", @router.post("/console/reset",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses=responses) responses=responses)
async def reset_console_all(project: Project = Depends(dep_project)): async def reset_console_all_nodes(project: Project = Depends(dep_project)):
""" """
Reset console for all nodes belonging to the project. Reset console for all nodes belonging to the project.
""" """

View File

@ -347,7 +347,7 @@ async def import_project(project_id: UUID, request: Request, path: Optional[Path
**responses, **responses,
409: {"model": schemas.ErrorMessage, "description": "Could not duplicate project"} 409: {"model": schemas.ErrorMessage, "description": "Could not duplicate project"}
}) })
async def duplicate(project_data: schemas.ProjectDuplicate, project: Project = Depends(dep_project)): async def duplicate_project(project_data: schemas.ProjectDuplicate, project: Project = Depends(dep_project)):
""" """
Duplicate a project. Duplicate a project.
""" """

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -0,0 +1,125 @@
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
API routes for users.
"""
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from uuid import UUID
from typing import List
from gns3server import schemas
from gns3server.controller.controller_error import ControllerBadRequestError, ControllerNotFoundError
from gns3server.db.repositories.users import UsersRepository
from gns3server.services import auth_service
from .dependencies.authentication import get_current_active_user
import logging
log = logging.getLogger(__name__)
router = APIRouter()
@router.get("", response_model=List[schemas.User])
async def get_users(user_repo: UsersRepository = Depends()) -> List[schemas.User]:
"""
Get all users.
"""
users = await user_repo.get_users()
return users
@router.post("", response_model=schemas.User, status_code=status.HTTP_201_CREATED)
async def create_user(new_user: schemas.UserCreate, user_repo: UsersRepository = Depends()) -> schemas.User:
"""
Create a new user.
"""
if await user_repo.get_user_by_username(new_user.username):
raise ControllerBadRequestError(f"Username '{new_user.username}' is already registered")
if new_user.email and await user_repo.get_user_by_email(new_user.email):
raise ControllerBadRequestError(f"Email '{new_user.email}' is already registered")
return await user_repo.create_user(new_user)
@router.get("/{user_id}", response_model=schemas.User)
async def get_user(user_id: UUID, user_repo: UsersRepository = Depends()) -> schemas.User:
"""
Get an user.
"""
user = await user_repo.get_user(user_id)
if not user:
raise ControllerNotFoundError(f"User '{user_id}' not found")
return user
@router.put("/{user_id}", response_model=schemas.User)
async def update_user(user_id: UUID,
update_user: schemas.UserUpdate,
user_repo: UsersRepository = Depends()) -> schemas.User:
"""
Update an user.
"""
user = await user_repo.update_user(user_id, update_user)
if not user:
raise ControllerNotFoundError(f"User '{user_id}' not found")
return user
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(user_id: UUID, user_repo: UsersRepository = Depends()):
"""
Delete an user.
"""
success = await user_repo.delete_user(user_id)
if not success:
raise ControllerNotFoundError(f"User '{user_id}' not found")
@router.post("/login", response_model=schemas.Token)
async def login(user_repo: UsersRepository = Depends(),
form_data: OAuth2PasswordRequestForm = Depends()) -> schemas.Token:
"""
User login.
"""
user = await user_repo.authenticate_user(username=form_data.username, password=form_data.password)
if not user:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authentication was unsuccessful.",
headers={"WWW-Authenticate": "Bearer"})
token = schemas.Token(access_token=auth_service.create_access_token(user.username), token_type="bearer")
return token
@router.get("/users/me/", response_model=schemas.User)
async def get_current_active_user(current_user: schemas.User = Depends(get_current_active_user)) -> schemas.User:
"""
Get the current active user.
"""
return current_user

View File

@ -30,6 +30,7 @@ from fastapi.responses import JSONResponse
from gns3server.controller.controller_error import ( from gns3server.controller.controller_error import (
ControllerError, ControllerError,
ControllerNotFoundError, ControllerNotFoundError,
ControllerBadRequestError,
ControllerTimeoutError, ControllerTimeoutError,
ControllerForbiddenError, ControllerForbiddenError,
ControllerUnauthorizedError ControllerUnauthorizedError
@ -119,6 +120,15 @@ async def controller_not_found_error_handler(request: Request, exc: ControllerNo
) )
@app.exception_handler(ControllerBadRequestError)
async def controller_bad_request_error_handler(request: Request, exc: ControllerBadRequestError):
log.error(f"Controller bad request error: {exc}")
return JSONResponse(
status_code=400,
content={"message": str(exc)},
)
# make sure the content key is "message", not "detail" per default # make sure the content key is "message", not "detail" per default
@app.exception_handler(StarletteHTTPException) @app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException): async def http_exception_handler(request: Request, exc: StarletteHTTPException):

View File

@ -35,6 +35,12 @@ class ControllerNotFoundError(ControllerError):
super().__init__(message) super().__init__(message)
class ControllerBadRequestError(ControllerError):
def __init__(self, message: str):
super().__init__(message)
class ControllerUnauthorizedError(ControllerError): class ControllerUnauthorizedError(ControllerError):
def __init__(self, message: str): def __init__(self, message: str):

View File

@ -100,12 +100,12 @@ class Template:
try: try:
template_schema = TEMPLATE_TYPE_TO_SHEMA[self.template_type] template_schema = TEMPLATE_TYPE_TO_SHEMA[self.template_type]
template_settings_with_defaults = template_schema.parse_obj(self.__json__()) template_settings_with_defaults = template_schema.parse_obj(self.__json__())
self._settings = template_settings_with_defaults.dict() self._settings = jsonable_encoder(template_settings_with_defaults.dict())
if self.template_type == "dynamips": if self.template_type == "dynamips":
# special case for Dynamips to cover all platform types that contain specific settings # special case for Dynamips to cover all platform types that contain specific settings
dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SHEMA[self._settings["platform"]] dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SHEMA[self._settings["platform"]]
dynamips_template_settings_with_defaults = dynamips_template_schema.parse_obj(self.__json__()) dynamips_template_settings_with_defaults = dynamips_template_schema.parse_obj(self.__json__())
self._settings = dynamips_template_settings_with_defaults.dict() self._settings = jsonable_encoder(dynamips_template_settings_with_defaults.dict())
except ValidationError as e: except ValidationError as e:
print(e) #TODO: handle errors print(e) #TODO: handle errors
raise raise

View File

@ -25,7 +25,8 @@ from gns3server.controller import Controller
from gns3server.compute import MODULES from gns3server.compute import MODULES
from gns3server.compute.port_manager import PortManager from gns3server.compute.port_manager import PortManager
from gns3server.utils.http_client import HTTPClient from gns3server.utils.http_client import HTTPClient
#from gns3server.db.tasks import connect_to_db, close_db_connection from gns3server.db.tasks import connect_to_db
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -54,7 +55,7 @@ def create_startup_handler(app: FastAPI) -> Callable:
loop.set_debug(True) loop.set_debug(True)
# connect to the database # connect to the database
# await connect_to_db(app) await connect_to_db()
await Controller.instance().start() await Controller.instance().start()
# Because with a large image collection # Because with a large image collection
@ -89,7 +90,4 @@ def create_shutdown_handler(app: FastAPI) -> Callable:
if PortManager.instance().udp_ports: if PortManager.instance().udp_ports:
log.warning("UDP ports are still used {}".format(PortManager.instance().udp_ports)) log.warning("UDP ports are still used {}".format(PortManager.instance().udp_ports))
# close the connection to the database
# await close_db_connection(app)
return shutdown_handler return shutdown_handler

26
gns3server/db/database.py Normal file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import declarative_base
SQLALCHEMY_DATABASE_URL = os.environ.get("DATABASE_URI", "sqlite:///./sql_app.db")
engine = create_async_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
Base = declarative_base()

95
gns3server/db/models.py Normal file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import uuid
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, DateTime, func
from sqlalchemy.orm import relationship
from sqlalchemy.types import TypeDecorator, CHAR
from sqlalchemy.dialects.postgresql import UUID
from .database import Base
class GUID(TypeDecorator):
"""Platform-independent GUID type.
Uses PostgreSQL's UUID type, otherwise uses
CHAR(32), storing as stringified hex values.
"""
impl = CHAR
def load_dialect_impl(self, dialect):
if dialect.name == 'postgresql':
return dialect.type_descriptor(UUID())
else:
return dialect.type_descriptor(CHAR(32))
def process_bind_param(self, value, dialect):
if value is None:
return value
elif dialect.name == 'postgresql':
return str(value)
else:
if not isinstance(value, uuid.UUID):
return "%.32x" % uuid.UUID(value).int
else:
# hexstring
return "%.32x" % value.int
def process_result_value(self, value, dialect):
if value is None:
return value
else:
if not isinstance(value, uuid.UUID):
value = uuid.UUID(value)
return value
class BaseTable(Base):
__abstract__ = True
created_at = Column(DateTime, default=func.current_timestamp())
updated_at = Column(DateTime,
default=func.current_timestamp(),
onupdate=func.current_timestamp())
class User(BaseTable):
__tablename__ = "users"
user_id = Column(GUID, primary_key=True, default=str(uuid.uuid4()))
username = Column(String, unique=True, index=True)
email = Column(String, unique=True, index=True)
full_name = Column(String)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
# items = relationship("Item", back_populates="owner")
#
#
# class Item(Base):
# __tablename__ = "items"
#
# id = Column(Integer, primary_key=True, index=True)
# title = Column(String, index=True)
# description = Column(String, index=True)
# owner_id = Column(Integer, ForeignKey("users.id"))
#
# owner = relationship("User", back_populates="items")

View File

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy.ext.asyncio import AsyncSession
from ..database import engine
class BaseRepository:
async def db(self):
session = AsyncSession(engine)
try:
yield session
finally:
await session.close()

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from uuid import UUID
from typing import Optional, List
from sqlalchemy import select, update, delete
from sqlalchemy.ext.asyncio import AsyncSession
from ..database import engine
from .base import BaseRepository
import gns3server.db.models as models
from gns3server import schemas
from gns3server.services import auth_service
class UsersRepository(BaseRepository):
def __init__(self) -> None:
super().__init__()
self._auth_service = auth_service
async def get_user(self, user_id: UUID) -> Optional[models.User]:
async with AsyncSession(engine) as session:
result = await session.execute(select(models.User).where(models.User.user_id == user_id))
return result.scalars().first()
async def get_user_by_username(self, username: str) -> Optional[models.User]:
async with AsyncSession(engine) as session:
result = await session.execute(select(models.User).where(models.User.username == username))
return result.scalars().first()
async def get_user_by_email(self, email: str) -> Optional[models.User]:
async with AsyncSession(engine) as session:
result = await session.execute(select(models.User).where(models.User.email == email))
return result.scalars().first()
async def get_users(self) -> List[models.User]:
async with AsyncSession(engine) as session:
result = await session.execute(select(models.User))
return result.scalars().all()
async def create_user(self, user: schemas.UserCreate) -> models.User:
async with AsyncSession(engine) as session:
hashed_password = self._auth_service.hash_password(user.password)
db_user = models.User(username=user.username,
email=user.email,
full_name=user.full_name,
hashed_password=hashed_password)
session.add(db_user)
await session.commit()
await session.refresh(db_user)
return db_user
async def update_user(self, user_id: UUID, user_update: schemas.UserUpdate) -> Optional[models.User]:
async with AsyncSession(engine) as session:
update_values = user_update.dict(exclude_unset=True)
password = update_values.pop("password", None)
if password:
update_values["hashed_password"] = self._auth_service.hash_password(password=password)
print(update_values)
query = update(models.User) \
.where(models.User.user_id == user_id) \
.values(update_values)
await session.execute(query)
await session.commit()
return await self.get_user(user_id)
async def delete_user(self, user_id: UUID) -> bool:
async with AsyncSession(engine) as session:
query = delete(models.User).where(models.User.user_id == user_id)
result = await session.execute(query)
await session.commit()
return result.rowcount > 0
#except:
# await session.rollback()
async def authenticate_user(self, username: str, password: str) -> Optional[models.User]:
user = await self.get_user_by_username(username)
if not user:
return None
if not self._auth_service.verify_password(password, user.hashed_password):
return None
return user

34
gns3server/db/tasks.py Normal file
View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy.exc import SQLAlchemyError
from .database import engine
from .models import Base
import logging
log = logging.getLogger(__name__)
async def connect_to_db() -> None:
try:
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
log.info("Successfully connected to the database")
except SQLAlchemyError as e:
log.error(f"Error while connecting to the database: {e}")

View File

@ -26,6 +26,8 @@ from .drawings import Drawing
from .gns3vm import GNS3VM from .gns3vm import GNS3VM
from .nodes import NodeUpdate, NodeDuplicate, NodeCapture, Node from .nodes import NodeUpdate, NodeDuplicate, NodeCapture, Node
from .projects import ProjectCreate, ProjectUpdate, ProjectDuplicate, Project, ProjectFile from .projects import ProjectCreate, ProjectUpdate, ProjectDuplicate, Project, ProjectFile
from .users import UserCreate, UserUpdate, User
from .tokens import Token
from .snapshots import SnapshotCreate, Snapshot from .snapshots import SnapshotCreate, Snapshot
from .capabilities import Capabilities from .capabilities import Capabilities
from .nios import UDPNIO, TAPNIO, EthernetNIO from .nios import UDPNIO, TAPNIO, EthernetNIO

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
from datetime import datetime
from pydantic import BaseModel
class DateTimeModelMixin(BaseModel):
created_at: Optional[datetime]
updated_at: Optional[datetime]

View File

@ -24,14 +24,14 @@ from typing import Optional, List
from enum import Enum from enum import Enum
DEFAULT_PORTS = [ DEFAULT_PORTS = [
dict(port_number=0, name="Ethernet0", vlan=1, type="access", ethertype=""), dict(port_number=0, name="Ethernet0", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=1, name="Ethernet1", vlan=1, type="access", ethertype=""), dict(port_number=1, name="Ethernet1", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=2, name="Ethernet2", vlan=1, type="access", ethertype=""), dict(port_number=2, name="Ethernet2", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=3, name="Ethernet3", vlan=1, type="access", ethertype=""), dict(port_number=3, name="Ethernet3", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=4, name="Ethernet4", vlan=1, type="access", ethertype=""), dict(port_number=4, name="Ethernet4", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=5, name="Ethernet5", vlan=1, type="access", ethertype=""), dict(port_number=5, name="Ethernet5", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=6, name="Ethernet6", vlan=1, type="access", ethertype=""), dict(port_number=6, name="Ethernet6", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=7, name="Ethernet7", vlan=1, type="access", ethertype="") dict(port_number=7, name="Ethernet7", vlan=1, type="access", ethertype="0x8100")
] ]

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
from pydantic import BaseModel
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional
from pydantic import EmailStr, BaseModel, Field
from uuid import UUID
from .base import DateTimeModelMixin
class UserBase(BaseModel):
"""
Common user properties.
"""
username: Optional[str] = Field(None, min_length=3, regex="[a-zA-Z0-9_-]+$")
email: Optional[EmailStr]
full_name: Optional[str]
class UserCreate(UserBase):
"""
Properties to create an user.
"""
username: str = Field(..., min_length=3, regex="[a-zA-Z0-9_-]+$")
password: str = Field(..., min_length=7, max_length=100)
class UserUpdate(UserBase):
"""
Properties to update an user.
"""
password: Optional[str] = Field(None, min_length=7, max_length=100)
class User(DateTimeModelMixin, UserBase):
user_id: UUID
is_active: bool = True
is_superuser: bool = False
class Config:
orm_mode = True

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .authentication import AuthService
auth_service = AuthService()

View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import bcrypt
from jose import JWTError, jwt
from datetime import datetime, timedelta
from passlib.context import CryptContext
from typing import Optional
from fastapi import HTTPException, status
from gns3server.schemas.tokens import TokenData
from pydantic import ValidationError
# FIXME: temporary variables to move to config
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
#class AuthException(BaseException):
# pass
class AuthService:
def hash_password(self, password: str) -> str:
return pwd_context.hash(password)
def verify_password(self, password, hashed_password) -> bool:
return pwd_context.verify(password, hashed_password)
def create_access_token(self, username):
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode = {"sub": username, "exp": expire}
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def get_username_from_token(self, token: str) -> Optional[str]:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except (JWTError, ValidationError):
raise credentials_exception
return token_data.username

View File

@ -10,3 +10,7 @@ psutil==5.7.3
async-timeout==3.0.1 async-timeout==3.0.1
distro==1.5.0 distro==1.5.0
py-cpuinfo==7.0.0 py-cpuinfo==7.0.0
sqlalchemy==1.4.0b1 # beta version with asyncio support
passlib[bcrypt]==1.7.2
python-jose==3.2.0
email-validator==1.1.2

View File

@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Base code use for all API tests
"""
import json
import pytest
class Query:
"""
Helper to make queries against the test server
"""
def __init__(self, http_client, ws_client, prefix='', api_version=None):
"""
:param prefix: Prefix added before path (ex: /compute)
:param api_version: Version of the API
"""
self._http_client = http_client
self._ws_client = ws_client
self._prefix = prefix
self._api_version = api_version
def post(self, path, body={}, **kwargs):
return self._request("POST", path, body, **kwargs)
def put(self, path, body={}, **kwargs):
return self._request("PUT", path, body, **kwargs)
def get(self, path, **kwargs):
return self._request("GET", path, **kwargs)
def delete(self, path, **kwargs):
return self._request("DELETE", path, **kwargs)
def patch(self, path, **kwargs):
return self._request("PATCH", path, **kwargs)
def ws(self, path):
return self._ws_client.websocket_connect(self.get_url(path))
def get_url(self, path):
if self._api_version is None:
return "/{}{}".format(self._prefix, path)
return "/v{}{}{}".format(self._api_version, self._prefix, path)
@pytest.mark.asyncio
async def _request(self, method, path, body=None, raw=False, **kwargs):
if body is not None and raw is False:
body = json.dumps(body)
async with self._http_client as ac:
response = await ac.request(method, self.get_url(path), data=body, **kwargs)
#response.body = await response.read()
# x_route = response.headers.get('X-Route', None)
# if x_route is not None:
# response.route = x_route.replace("/v{}".format(self._api_version), "")
# response.route = response.route.replace(self._prefix, "")
#response.json = {}
#response.html = ""
if response.content is not None:
if response.headers.get("content-type") == "application/json":
try:
response.json = response.json()
except ValueError:
response.json = None
# else:
# try:
# response.html = response.text
# except UnicodeDecodeError:
# response.html = None
return response

View File

@ -20,35 +20,38 @@ import sys
import pytest import pytest
import psutil import psutil
from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.version import __version__ from gns3server.version import __version__
from gns3server.utils.path import get_default_project_directory from gns3server.utils.path import get_default_project_directory
pytestmark = pytest.mark.asyncio
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@pytest.mark.asyncio
async def test_get(compute_api, windows_platform):
response = await compute_api.get('/capabilities')
assert response.status_code == 200
assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'],
'version': __version__,
'platform': sys.platform,
'cpus': psutil.cpu_count(logical=True),
'memory': psutil.virtual_memory().total,
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
}
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@pytest.mark.asyncio async def test_get(app: FastAPI, client: AsyncClient, windows_platform) -> None:
async def test_get_on_gns3vm(compute_api, on_gns3vm):
response = await compute_api.get('/capabilities') response = await client.get(app.url_path_for("get_capabilities"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'], assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'],
'version': __version__, 'version': __version__,
'platform': sys.platform, 'platform': sys.platform,
'cpus': psutil.cpu_count(logical=True), 'cpus': psutil.cpu_count(logical=True),
'memory': psutil.virtual_memory().total, 'memory': psutil.virtual_memory().total,
'disk_size': psutil.disk_usage(get_default_project_directory()).total, 'disk_size': psutil.disk_usage(get_default_project_directory()).total,
} }
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
async def test_get_on_gns3vm(app: FastAPI, client: AsyncClient, on_gns3vm) -> None:
response = await client.get(app.url_path_for("get_capabilities"))
assert response.status_code == status.HTTP_200_OK
assert response.json() == {'node_types': ['cloud', 'ethernet_hub', 'ethernet_switch', 'nat', 'vpcs', 'virtualbox', 'dynamips', 'frame_relay_switch', 'atm_switch', 'qemu', 'vmware', 'traceng', 'docker', 'iou'],
'version': __version__,
'platform': sys.platform,
'cpus': psutil.cpu_count(logical=True),
'memory': psutil.virtual_memory().total,
'disk_size': psutil.disk_usage(get_default_project_directory()).total,
}

View File

@ -17,99 +17,126 @@
import pytest import pytest
from unittest.mock import patch from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, on_gns3vm) -> dict:
async def vm(compute_api, compute_project, on_gns3vm):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = await compute_api.post("/projects/{project_id}/cloud/nodes".format(project_id=compute_project.id), {"name": "Cloud 1"}) response = await client.post(app.url_path_for("create_cloud", project_id=compute_project.id),
assert response.status_code == 201 json={"name": "Cloud 1"})
return response.json assert response.status_code == status.HTTP_201_CREATED
return response.json()
@pytest.mark.asyncio async def test_cloud_create(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_cloud_create(compute_api, compute_project):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = await compute_api.post("/projects/{project_id}/cloud/nodes".format(project_id=compute_project.id), {"name": "Cloud 1"}) response = await client.post(app.url_path_for("create_cloud", project_id=compute_project.id),
json={"name": "Cloud 1"})
assert response.status_code == 201 assert response.status_code == 201
assert response.json["name"] == "Cloud 1" assert response.json()["name"] == "Cloud 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_get_cloud(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_cloud_get(compute_api, compute_project, vm):
response = await compute_api.get("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.get(app.url_path_for("get_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "Cloud 1" assert response.json()["name"] == "Cloud 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["status"] == "started" assert response.json()["status"] == "started"
@pytest.mark.asyncio async def test_cloud_nio_create_udp(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_cloud_nio_create_udp(compute_api, vm):
params = {"type": "nio_udp", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "rhost": "127.0.0.1"}
response = await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_cloud_nio",
assert response.status_code == 201 project_id=vm["project_id"],
assert response.json["type"] == "nio_udp" node_id=vm["node_id"],
adapter_number="0",
port_number="0")
response = await client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_cloud_nio_update_udp(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_cloud_nio_update_udp(compute_api, vm):
params = {"type": "nio_udp", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "rhost": "127.0.0.1"}
await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_cloud_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
await client.post(url, json=params)
params["filters"] = {} params["filters"] = {}
response = await compute_api.put("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_cloud_nio",
assert response.status_code == 201, response.body.decode() project_id=vm["project_id"],
assert response.json["type"] == "nio_udp" node_id=vm["node_id"],
adapter_number="0",
port_number="0")
response = await client.put(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_cloud_delete_nio(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_cloud_delete_nio(compute_api, vm):
params = {"type": "nio_udp", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "rhost": "127.0.0.1"}
await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_cloud_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
await client.post(url, json=params)
url = app.url_path_for("delete_cloud_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"): with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud._start_ubridge"):
response = await compute_api.delete("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_cloud_delete(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_cloud_delete(compute_api, vm):
response = await compute_api.delete("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(app.url_path_for("delete_cloud", project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_cloud_update(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_cloud_update(compute_api, vm):
response = await compute_api.put("/projects/{project_id}/cloud/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test"}) response = await client.put(app.url_path_for("update_cloud", project_id=vm["project_id"], node_id=vm["node_id"]),
assert response.status_code == 200 json={"name": "test"})
assert response.json["name"] == "test" assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test"
@pytest.mark.asyncio async def test_cloud_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_cloud_start_capture(compute_api, vm):
params = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
@ -117,18 +144,26 @@ async def test_cloud_start_capture(compute_api, vm):
} }
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock: with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(app.url_path_for("start_cloud_capture",
assert response.status_code == 200 project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0"),
json=params)
assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio async def test_cloud_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_cloud_stop_capture(compute_api, vm):
with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock: with asyncio_patch("gns3server.compute.builtin.nodes.cloud.Cloud.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/cloud/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("stop_cloud_capture",
assert response.status_code == 204 project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0"))
assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called

View File

@ -18,34 +18,35 @@
import os import os
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.version import __version__ from gns3server.version import __version__
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.mark.asyncio async def test_udp_allocation(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_udp_allocation(compute_api, compute_project):
response = await compute_api.post('/projects/{}/ports/udp'.format(compute_project.id), {}) response = await client.post(app.url_path_for("allocate_udp_port", project_id=compute_project.id), json={})
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json['udp_port'] is not None assert response.json()['udp_port'] is not None
# Netfifaces is not available on Travis async def test_interfaces(app: FastAPI, client: AsyncClient) -> None:
@pytest.mark.skipif(os.environ.get("TRAVIS", False) is not False, reason="Not supported on Travis")
@pytest.mark.asyncio
async def test_interfaces(compute_api):
response = await compute_api.get('/network/interfaces') response = await client.get(app.url_path_for("network_interfaces"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert isinstance(response.json, list) assert isinstance(response.json(), list)
@pytest.mark.asyncio async def test_version_output(app: FastAPI, client: AsyncClient, config) -> None:
async def test_version_output(compute_api, config):
config.set("Server", "local", "true") config.set("Server", "local", "true")
response = await compute_api.get('/version') response = await client.get(app.url_path_for("compute_version"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == {'local': True, 'version': __version__} assert response.json() == {'local': True, 'version': __version__}
# @pytest.mark.asyncio # @pytest.mark.asyncio
@ -55,8 +56,7 @@ async def test_version_output(compute_api, config):
# assert response.status_code == 200 # assert response.status_code == 200
@pytest.mark.asyncio async def test_statistics_output(app: FastAPI, client: AsyncClient) -> None:
async def test_statistics_output(compute_api):
response = await compute_api.get('/statistics') response = await client.get(app.url_path_for("compute_statistics"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK

View File

@ -17,16 +17,20 @@
import pytest import pytest
import sys import sys
import uuid
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") from gns3server.compute.project import Project
pytestmark = [pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows"),
pytest.mark.asyncio]
@pytest.fixture @pytest.fixture
def base_params(): def base_params() -> dict:
"""Return standard parameters""" """Return standard parameters"""
params = { params = {
@ -53,90 +57,96 @@ def base_params():
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> dict:
async def vm(compute_api, compute_project, base_params):
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"): with asyncio_patch("gns3server.compute.docker.DockerVM._get_container_state", return_value="exited"):
response = await compute_api.post("/projects/{project_id}/docker/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_docker_node", project_id=compute_project.id),
assert response.status_code == 201 json=base_params)
return response.json assert response.status_code == status.HTTP_201_CREATED
return response.json()
@pytest.mark.asyncio async def test_docker_create(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> None:
async def test_docker_create(compute_api, compute_project, base_params):
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
response = await compute_api.post("/projects/{project_id}/docker/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_docker_node", project_id=compute_project.id),
assert response.status_code == 201 json=base_params)
assert response.json["name"] == "PC TEST 1" assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == compute_project.id assert response.json()["name"] == "PC TEST 1"
assert response.json["container_id"] == "8bd8153ea8f5" assert response.json()["project_id"] == compute_project.id
assert response.json["image"] == "nginx:latest" assert response.json()["container_id"] == "8bd8153ea8f5"
assert response.json["adapters"] == 2 assert response.json()["image"] == "nginx:latest"
assert response.json["environment"] == "YES=1\nNO=0" assert response.json()["adapters"] == 2
assert response.json["console_resolution"] == "1280x1024" assert response.json()["environment"] == "YES=1\nNO=0"
assert response.json["extra_hosts"] == "test:127.0.0.1" assert response.json()["console_resolution"] == "1280x1024"
assert response.json()["extra_hosts"] == "test:127.0.0.1"
@pytest.mark.asyncio async def test_docker_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_start(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
response = await client.post(app.url_path_for("start_docker_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_docker_stop(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("stop_docker_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_docker_reload(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.restart", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("reload_docker_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_docker_delete(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.delete", return_value=True) as mock:
response = await compute_api.delete("/projects/{project_id}/docker/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(app.url_path_for("delete_docker_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_docker_pause(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_pause(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.pause", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/pause".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("pause_docker_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_docker_unpause(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_unpause(compute_api, vm):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.unpause", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/unpause".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("unpause_docker_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_docker_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_nio_create_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -144,13 +154,17 @@ async def test_docker_nio_create_udp(compute_api, vm):
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "rhost": "127.0.0.1"}
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_docker_node_nio",
assert response.status_code == 201 project_id=vm["project_id"],
assert response.json["type"] == "nio_udp" node_id=vm["node_id"],
adapter_number="0",
port_number="0")
response = await client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_docker_update_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_update_nio(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -159,23 +173,37 @@ async def test_docker_update_nio(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_docker_node_nio",
assert response.status_code == 201 project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
response = await client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
url = app.url_path_for("update_docker_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"): with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_update_nio_binding"):
response = await compute_api.put("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.put(url, json=params)
assert response.status_code == 201, response.body.decode() assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.asyncio async def test_docker_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_delete_nio(compute_api, vm):
url = app.url_path_for("delete_docker_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"): with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.adapter_remove_nio_binding"):
response = await compute_api.delete("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_docker_update(app: FastAPI, client: AsyncClient, vm: dict, free_console_port: int) -> None:
async def test_docker_update(compute_api, vm, free_console_port):
params = { params = {
"name": "test", "name": "test",
@ -186,48 +214,62 @@ async def test_docker_update(compute_api, vm, free_console_port):
} }
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.update") as mock:
response = await compute_api.put("/projects/{project_id}/docker/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.put(app.url_path_for("update_docker_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json=params)
assert response.status_code == 200 assert response.status_code == 200
assert mock.called assert mock.called
assert response.json["name"] == "test" assert response.json()["name"] == "test"
assert response.json["console"] == free_console_port assert response.json()["console"] == free_console_port
assert response.json["start_command"] == "yes" assert response.json()["start_command"] == "yes"
assert response.json["environment"] == "GNS3=1\nGNS4=0" assert response.json()["environment"] == "GNS3=1\nGNS4=0"
assert response.json["extra_hosts"] == "test:127.0.0.1" assert response.json()["extra_hosts"] == "test:127.0.0.1"
@pytest.mark.asyncio async def test_docker_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_start_capture(compute_api, vm):
url = app.url_path_for("start_docker_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True): with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.start_capture") as mock:
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"} params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params) response = await client.post(url, json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio async def test_docker_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_docker_stop_capture(compute_api, vm):
url = app.url_path_for("stop_docker_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True): with patch("gns3server.compute.docker.docker_vm.DockerVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as mock: with asyncio_patch("gns3server.compute.docker.docker_vm.DockerVM.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
@pytest.mark.asyncio async def test_docker_duplicate(app: FastAPI, client: AsyncClient, vm: dict, base_params: dict) -> None:
async def test_docker_duplicate(compute_api, compute_project, base_params, vm):
# create destination node first # create destination node first
with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]): with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "nginx"}]):
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value={"Id": "8bd8153ea8f5"}):
response = await compute_api.post("/projects/{project_id}/docker/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_docker_node",
assert response.status_code == 201 project_id=vm["project_id"]), json=base_params)
assert response.status_code == status.HTTP_201_CREATED
params = {"destination_node_id": response.json["node_id"]} params = {"destination_node_id": response.json()["node_id"]}
response = await compute_api.post("/projects/{project_id}/docker/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(app.url_path_for("duplicate_docker_node",
assert response.status_code == 201 project_id=vm["project_id"],
node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED

View File

@ -19,10 +19,12 @@ import pytest
import sys import sys
import os import os
import stat import stat
from unittest.mock import patch from unittest.mock import patch
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch pytestmark = pytest.mark.asyncio
# @pytest.yield_fixture(scope="module") # @pytest.yield_fixture(scope="module")
# async def vm(compute_api, compute_project, fake_image): # async def vm(compute_api, compute_project, fake_image):
@ -139,7 +141,7 @@ from tests.utils import asyncio_patch
@pytest.fixture @pytest.fixture
def fake_image(tmpdir): def fake_image(tmpdir) -> str:
"""Create a fake Dynamips image on disk""" """Create a fake Dynamips image on disk"""
path = str(tmpdir / "7200.bin") path = str(tmpdir / "7200.bin")
@ -150,7 +152,7 @@ def fake_image(tmpdir):
@pytest.fixture @pytest.fixture
def fake_file(tmpdir): def fake_file(tmpdir) -> str:
"""Create a fake file disk""" """Create a fake file disk"""
path = str(tmpdir / "7200.txt") path = str(tmpdir / "7200.txt")
@ -160,24 +162,21 @@ def fake_file(tmpdir):
return path return path
@pytest.mark.asyncio async def test_images(app: FastAPI, client: AsyncClient, tmpdir, fake_image: str, fake_file: str) -> None:
async def test_images(compute_api, tmpdir, fake_image, fake_file):
with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)): with patch("gns3server.utils.images.default_images_directory", return_value=str(tmpdir)):
response = await compute_api.get("/dynamips/images") response = await client.get(app.url_path_for("get_dynamips_images"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == [{"filename": "7200.bin", assert response.json() == [{"filename": "7200.bin",
"path": "7200.bin", "path": "7200.bin",
"filesize": 7, "filesize": 7,
"md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e" "md5sum": "b0d5aa897d937aced5a6b1046e8f7e2e"}]
}]
@pytest.mark.asyncio async def test_upload_image(app: FastAPI, client: AsyncClient, images_dir: str) -> None:
async def test_upload_image(compute_api, images_dir):
response = await compute_api.post("/dynamips/images/test2", body=b"TEST", raw=True) response = await client.post(app.url_path_for("upload_dynamips_image", filename="test2"), content=b"TEST")
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
with open(os.path.join(images_dir, "IOS", "test2")) as f: with open(os.path.join(images_dir, "IOS", "test2")) as f:
assert f.read() == "TEST" assert f.read() == "TEST"
@ -188,13 +187,12 @@ async def test_upload_image(compute_api, images_dir):
@pytest.mark.skipif(not sys.platform.startswith("win") and os.getuid() == 0, reason="Root can delete any image") @pytest.mark.skipif(not sys.platform.startswith("win") and os.getuid() == 0, reason="Root can delete any image")
@pytest.mark.asyncio async def test_upload_image_permission_denied(app: FastAPI, client: AsyncClient, images_dir: str) -> None:
async def test_upload_image_permission_denied(compute_api, images_dir):
os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True) os.makedirs(os.path.join(images_dir, "IOS"), exist_ok=True)
with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f: with open(os.path.join(images_dir, "IOS", "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0) os.chmod(os.path.join(images_dir, "IOS", "test2.tmp"), 0)
response = await compute_api.post("/dynamips/images/test2", body=b"TEST", raw=True) response = await client.post(app.url_path_for("upload_dynamips_image", filename="test2"), content=b"TEST")
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT

View File

@ -21,14 +21,19 @@ import stat
import sys import sys
import uuid import uuid
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
pytestmark = pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") from gns3server.compute.project import Project
pytestmark = [pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows"),
pytest.mark.asyncio]
@pytest.fixture @pytest.fixture
def fake_iou_bin(images_dir): def fake_iou_bin(images_dir) -> str:
"""Create a fake IOU image on disk""" """Create a fake IOU image on disk"""
path = os.path.join(images_dir, "IOU", "iou.bin") path = os.path.join(images_dir, "IOU", "iou.bin")
@ -39,44 +44,44 @@ def fake_iou_bin(images_dir):
@pytest.fixture @pytest.fixture
def base_params(tmpdir, fake_iou_bin): def base_params(tmpdir, fake_iou_bin) -> dict:
"""Return standard parameters""" """Return standard parameters"""
return {"application_id": 42, "name": "PC TEST 1", "path": "iou.bin"} return {"application_id": 42, "name": "PC TEST 1", "path": "iou.bin"}
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> dict:
async def vm(compute_api, compute_project, base_params):
response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
return response.json return response.json()
def startup_config_file(compute_project, vm): def startup_config_file(compute_project: Project, vm: dict) -> str:
directory = os.path.join(compute_project.path, "project-files", "iou", vm["node_id"]) directory = os.path.join(compute_project.path, "project-files", "iou", vm["node_id"])
os.makedirs(directory, exist_ok=True) os.makedirs(directory, exist_ok=True)
return os.path.join(directory, "startup-config.cfg") return os.path.join(directory, "startup-config.cfg")
@pytest.mark.asyncio async def test_iou_create(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> None:
async def test_iou_create(compute_api, compute_project, base_params):
response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["serial_adapters"] == 2 assert response.json()["serial_adapters"] == 2
assert response.json["ethernet_adapters"] == 2 assert response.json()["ethernet_adapters"] == 2
assert response.json["ram"] == 256 assert response.json()["ram"] == 256
assert response.json["nvram"] == 128 assert response.json()["nvram"] == 128
assert response.json["l1_keepalives"] is False assert response.json()["l1_keepalives"] is False
@pytest.mark.asyncio async def test_iou_create_with_params(app: FastAPI,
async def test_iou_create_with_params(compute_api, compute_project, base_params): client: AsyncClient,
compute_project: Project,
base_params: dict) -> None:
params = base_params params = base_params
params["ram"] = 1024 params["ram"] = 1024
@ -87,23 +92,26 @@ async def test_iou_create_with_params(compute_api, compute_project, base_params)
params["startup_config_content"] = "hostname test" params["startup_config_content"] = "hostname test"
params["use_default_iou_values"] = False params["use_default_iou_values"] = False
response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["serial_adapters"] == 4 assert response.json()["serial_adapters"] == 4
assert response.json["ethernet_adapters"] == 0 assert response.json()["ethernet_adapters"] == 0
assert response.json["ram"] == 1024 assert response.json()["ram"] == 1024
assert response.json["nvram"] == 512 assert response.json()["nvram"] == 512
assert response.json["l1_keepalives"] is True assert response.json()["l1_keepalives"] is True
assert response.json["use_default_iou_values"] is False assert response.json()["use_default_iou_values"] is False
with open(startup_config_file(compute_project, response.json)) as f: with open(startup_config_file(compute_project, response.json())) as f:
assert f.read() == "hostname test" assert f.read() == "hostname test"
@pytest.mark.asyncio async def test_iou_create_startup_config_already_exist(
async def test_iou_create_startup_config_already_exist(compute_api, compute_project, base_params): app: FastAPI,
client: AsyncClient,
compute_project: Project,
base_params: dict) -> None:
"""We don't erase a startup-config if already exist at project creation""" """We don't erase a startup-config if already exist at project creation"""
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
@ -115,78 +123,78 @@ async def test_iou_create_startup_config_already_exist(compute_api, compute_proj
params["node_id"] = node_id params["node_id"] = node_id
params["startup_config_content"] = "hostname test" params["startup_config_content"] = "hostname test"
response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_iou_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
with open(startup_config_file(compute_project, response.json)) as f: with open(startup_config_file(compute_project, response.json())) as f:
assert f.read() == "echo hello" assert f.read() == "echo hello"
@pytest.mark.asyncio async def test_iou_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_iou_get(compute_api, compute_project, vm):
response = await compute_api.get("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.get(app.url_path_for("get_iou_node", project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["serial_adapters"] == 2 assert response.json()["serial_adapters"] == 2
assert response.json["ethernet_adapters"] == 2 assert response.json()["ethernet_adapters"] == 2
assert response.json["ram"] == 256 assert response.json()["ram"] == 256
assert response.json["nvram"] == 128 assert response.json()["nvram"] == 128
assert response.json["l1_keepalives"] is False assert response.json()["l1_keepalives"] is False
@pytest.mark.asyncio async def test_iou_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_start(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("start_iou_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json={})
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_iou_start_with_iourc(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_start_with_iourc(compute_api, vm):
params = {"iourc_content": "test"} params = {"iourc_content": "test"}
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(app.url_path_for("start_iou_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json=params)
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
response = await compute_api.get("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 200
@pytest.mark.asyncio async def test_iou_stop(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("stop_iou_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_iou_reload(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.reload", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("reload_iou_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_iou_delete(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.iou.IOU.delete_node", return_value=True) as mock:
response = await compute_api.delete("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(app.url_path_for("delete_iou_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_iou_update(app: FastAPI, client: AsyncClient, vm: dict, free_console_port: int) -> None:
async def test_iou_update(compute_api, vm, free_console_port):
params = { params = {
"name": "test", "name": "test",
@ -199,91 +207,122 @@ async def test_iou_update(compute_api, vm, free_console_port):
"use_default_iou_values": True, "use_default_iou_values": True,
} }
response = await compute_api.put("/projects/{project_id}/iou/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.put(app.url_path_for("update_iou_node",
assert response.status_code == 200 project_id=vm["project_id"],
assert response.json["name"] == "test" node_id=vm["node_id"]), json=params)
assert response.json["console"] == free_console_port assert response.status_code == status.HTTP_200_OK
assert response.json["ethernet_adapters"] == 4 assert response.json()["name"] == "test"
assert response.json["serial_adapters"] == 0 assert response.json()["console"] == free_console_port
assert response.json["ram"] == 512 assert response.json()["ethernet_adapters"] == 4
assert response.json["nvram"] == 2048 assert response.json()["serial_adapters"] == 0
assert response.json["l1_keepalives"] is True assert response.json()["ram"] == 512
assert response.json["use_default_iou_values"] is True assert response.json()["nvram"] == 2048
assert response.json()["l1_keepalives"] is True
assert response.json()["use_default_iou_values"] is True
@pytest.mark.asyncio async def test_iou_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_nio_create_udp(compute_api, vm):
params = {"type": "nio_udp", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "rhost": "127.0.0.1"}
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_iou_node_nio",
assert response.status_code == 201 project_id=vm["project_id"],
assert response.json["type"] == "nio_udp" node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_iou_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_nio_update_udp(compute_api, vm):
params = {"type": "nio_udp", params = {"type": "nio_udp",
"lport": 4242, "lport": 4242,
"rport": 4343, "rport": 4343,
"rhost": "127.0.0.1"} "rhost": "127.0.0.1"}
await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_iou_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
await client.post(url, json=params)
params["filters"] = {} params["filters"] = {}
response = await compute_api.put("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status_code == 201, response.body.decode() url = app.url_path_for("update_iou_node_nio",
assert response.json["type"] == "nio_udp" project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.put(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_iou_nio_create_ethernet(app: FastAPI, client: AsyncClient, vm: dict, ethernet_device: str) -> None:
async def test_iou_nio_create_ethernet(compute_api, vm, ethernet_device):
params = { params = {
"type": "nio_ethernet", "type": "nio_ethernet",
"ethernet_device": ethernet_device "ethernet_device": ethernet_device
} }
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_iou_node_nio",
assert response.status_code == 201 project_id=vm["project_id"],
assert response.json["type"] == "nio_ethernet" node_id=vm["node_id"],
assert response.json["ethernet_device"] == ethernet_device adapter_number="1",
port_number="0")
response = await client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_ethernet"
assert response.json()["ethernet_device"] == ethernet_device
@pytest.mark.asyncio async def test_iou_nio_create_ethernet_different_port(app: FastAPI,
async def test_iou_nio_create_ethernet_different_port(compute_api, vm, ethernet_device): client: AsyncClient,
vm: dict,
ethernet_device: str) -> None:
params = { params = {
"type": "nio_ethernet", "type": "nio_ethernet",
"ethernet_device": ethernet_device "ethernet_device": ethernet_device
} }
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/3/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_iou_node_nio",
assert response.status_code == 201 project_id=vm["project_id"],
assert response.json["type"] == "nio_ethernet" node_id=vm["node_id"],
assert response.json["ethernet_device"] == ethernet_device adapter_number="0",
port_number="3")
response = await client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_ethernet"
assert response.json()["ethernet_device"] == ethernet_device
@pytest.mark.asyncio async def test_iou_nio_create_tap(app: FastAPI, client: AsyncClient, vm: dict, ethernet_device: str) -> None:
async def test_iou_nio_create_tap(compute_api, vm, ethernet_device):
params = { params = {
"type": "nio_tap", "type": "nio_tap",
"tap_device": ethernet_device "tap_device": ethernet_device
} }
url = app.url_path_for("create_iou_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True): with patch("gns3server.compute.base_manager.BaseManager.has_privileged_access", return_value=True):
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["type"] == "nio_tap" assert response.json()["type"] == "nio_tap"
@pytest.mark.asyncio async def test_iou_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_delete_nio(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -292,33 +331,57 @@ async def test_iou_delete_nio(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_iou_node_nio",
response = await compute_api.delete("/projects/{project_id}/iou/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) project_id=vm["project_id"],
assert response.status_code == 204 node_id=vm["node_id"],
adapter_number="1",
port_number="0")
await client.post(url, json=params)
url = app.url_path_for("delete_iou_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_iou_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_start_capture(compute_api, vm):
params = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "data_link_type": "DLT_EN10MB"
} }
url = app.url_path_for("start_iou_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True): with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio async def test_iou_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_iou_stop_capture(compute_api, vm):
url = app.url_path_for("stop_iou_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True): with patch("gns3server.compute.iou.iou_vm.IOUVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as mock: with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
@ -327,24 +390,22 @@ async def test_iou_stop_capture(compute_api, vm):
# #
# with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"): # with asyncio_patch("gns3server.compute.iou.iou_vm.IOUVM.get_nio"):
# with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"): # with asyncio_patch("gns3server.compute.iou.IOU.stream_pcap_file"):
# response = await compute_api.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) # response = await client.get("/projects/{project_id}/iou/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
# assert response.status_code == 200 # assert response.status_code == status.HTTP_200_OK
@pytest.mark.asyncio async def test_images(app: FastAPI, client: AsyncClient, fake_iou_bin: str) -> None:
async def test_images(compute_api, fake_iou_bin):
response = await compute_api.get("/iou/images") response = await client.get(app.url_path_for("get_iou_images"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}] assert response.json() == [{"filename": "iou.bin", "path": "iou.bin", "filesize": 7, "md5sum": "e573e8f5c93c6c00783f20c7a170aa6c"}]
@pytest.mark.asyncio async def test_image_vm(app: FastAPI, client: AsyncClient, tmpdir) -> None:
async def test_image_vm(compute_api, tmpdir):
with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)): with patch("gns3server.compute.IOU.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/iou/images/test2", body="TEST", raw=True) response = await client.post(app.url_path_for("download_iou_image", filename="test2"), content=b"TEST")
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
with open(str(tmpdir / "test2")) as f: with open(str(tmpdir / "test2")) as f:
assert f.read() == "TEST" assert f.read() == "TEST"
@ -354,13 +415,15 @@ async def test_image_vm(compute_api, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
@pytest.mark.asyncio async def test_iou_duplicate(app: FastAPI, client: AsyncClient, vm: dict, base_params: dict) -> None:
async def test_iou_duplicate(compute_api, compute_project, vm, base_params):
# create destination node first # create destination node first
response = await compute_api.post("/projects/{project_id}/iou/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_iou_node", project_id=vm["project_id"]), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
params = {"destination_node_id": response.json["node_id"]} params = {"destination_node_id": response.json()["node_id"]}
response = await compute_api.post("/projects/{project_id}/iou/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status_code == 201 response = await client.post(app.url_path_for("duplicate_iou_node",
project_id=vm["project_id"],
node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED

View File

@ -17,41 +17,45 @@
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, ubridge_path: str, on_gns3vm) -> dict:
async def vm(compute_api, compute_project, ubridge_path, on_gns3vm):
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
response = await compute_api.post("/projects/{project_id}/nat/nodes".format(project_id=compute_project.id), {"name": "Nat 1"}) response = await client.post(app.url_path_for("create_nat_node", project_id=compute_project.id),
assert response.status_code == 201 json={"name": "Nat 1"})
return response.json assert response.status_code == status.HTTP_201_CREATED
return response.json()
@pytest.mark.asyncio async def test_nat_create(app: FastAPI, client: AsyncClient, compute_project: Project, on_gns3vm) -> None:
async def test_nat_create(compute_api, compute_project, on_gns3vm):
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat._start_ubridge"):
response = await compute_api.post("/projects/{project_id}/nat/nodes".format(project_id=compute_project.id), {"name": "Nat 1"}) response = await client.post(app.url_path_for("create_nat_node", project_id=compute_project.id),
assert response.status_code == 201 json={"name": "Nat 1"})
assert response.json["name"] == "Nat 1" assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == compute_project.id assert response.json()["name"] == "Nat 1"
assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_nat_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_nat_get(compute_api, compute_project, vm):
response = await compute_api.get("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.get(app.url_path_for("get_nat_node", project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "Nat 1" assert response.json()["name"] == "Nat 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["status"] == "started" assert response.json()["status"] == "started"
@pytest.mark.asyncio async def test_nat_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_nio_create_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -60,14 +64,19 @@ async def test_nat_nio_create_udp(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
url = app.url_path_for("create_nat_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
response = await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["type"] == "nio_udp" assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_nat_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_nio_update_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -76,15 +85,26 @@ async def test_nat_nio_update_udp(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_nat_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
await client.post(url, json=params)
params["filters"] = {} params["filters"] = {}
response = await compute_api.put("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status_code == 201, response.body.decode() url = app.url_path_for("update_nat_node_nio",
assert response.json["type"] == "nio_udp" project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
response = await client.put(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_nat_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_delete_nio(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -93,50 +113,73 @@ async def test_nat_delete_nio(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
url = app.url_path_for("create_nat_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"): with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.add_nio"):
await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) await client.post(url, json=params)
url = app.url_path_for("delete_nat_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock: with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.remove_nio") as mock:
response = await compute_api.delete("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(url)
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_nat_delete(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_delete(compute_api, vm):
response = await compute_api.delete("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(app.url_path_for("delete_nat_node",
assert response.status_code == 204 project_id=vm["project_id"],
node_id=vm["node_id"]))
assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_nat_update(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_update(compute_api, vm):
response = await compute_api.put("/projects/{project_id}/nat/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"name": "test"}) response = await client.put(app.url_path_for("update_nat_node",
assert response.status_code == 200 project_id=vm["project_id"],
assert response.json["name"] == "test" node_id=vm["node_id"]), json={"name": "test"})
assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test"
@pytest.mark.asyncio async def test_nat_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_start_capture(compute_api, vm):
params = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "data_link_type": "DLT_EN10MB"
} }
url = app.url_path_for("start_nat_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock: with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params) response = await client.post(url, json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio async def test_nat_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_nat_stop_capture(compute_api, vm):
url = app.url_path_for("stop_nat_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock: with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
@ -145,5 +188,5 @@ async def test_nat_stop_capture(compute_api, vm):
# #
# with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"): # with asyncio_patch("gns3server.compute.builtin.nodes.nat.Nat.get_nio"):
# with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"): # with asyncio_patch("gns3server.compute.builtin.Builtin.stream_pcap_file"):
# response = await compute_api.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) # response = await client.get("/projects/{project_id}/nat/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
# assert response.status_code == 200 # assert response.status_code == status.HTTP_200_OK

View File

@ -21,11 +21,11 @@ import json
from gns3server.compute.notification_manager import NotificationManager from gns3server.compute.notification_manager import NotificationManager
@pytest.mark.asyncio # @pytest.mark.asyncio
async def test_notification_ws(compute_api): # async def test_notification_ws(compute_api):
#
# FIXME: how to test websockets # # FIXME: how to test websockets
pass # pass
#with compute_api.ws("/notifications/ws") as ws: #with compute_api.ws("/notifications/ws") as ws:

View File

@ -21,12 +21,17 @@ import os
from unittest.mock import patch from unittest.mock import patch
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture @pytest.fixture
def base_params(tmpdir): def base_params(tmpdir) -> dict:
"""Return standard parameters""" """Return standard parameters"""
params = { params = {
@ -37,109 +42,103 @@ def base_params(tmpdir):
return params return params
@pytest.mark.asyncio async def test_create_project_with_path(app: FastAPI, client: AsyncClient, base_params: dict) -> None:
async def test_create_project_with_path(compute_api, base_params):
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
response = await compute_api.post("/projects", base_params) response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == base_params["project_id"] assert response.json()["project_id"] == base_params["project_id"]
@pytest.mark.asyncio async def test_create_project_with_path_and_empty_variables(app: FastAPI,
async def test_create_project_with_path_and_empty_variables(compute_api, base_params): client: AsyncClient,
base_params: dict) -> None:
base_params["variables"] = None base_params["variables"] = None
with patch("gns3server.compute.project.Project.is_local", return_value=True): with patch("gns3server.compute.project.Project.is_local", return_value=True):
response = await compute_api.post("/projects", base_params) response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == base_params["project_id"] assert response.json()["project_id"] == base_params["project_id"]
@pytest.mark.asyncio async def test_create_project_without_dir(app: FastAPI, client: AsyncClient, base_params: dict) -> None:
async def test_create_project_without_dir(compute_api, base_params):
del base_params["path"] del base_params["path"]
response = await compute_api.post("/projects", base_params) response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == base_params["project_id"] assert response.json()["project_id"] == base_params["project_id"]
assert response.json["name"] == base_params["name"] assert response.json()["name"] == base_params["name"]
@pytest.mark.asyncio async def test_show_project(app: FastAPI, client: AsyncClient, base_params: dict) -> None:
async def test_show_project(compute_api, base_params):
response = await compute_api.post("/projects", base_params) response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await compute_api.get("/projects/{project_id}".format(project_id=base_params["project_id"])) response = await client.get(app.url_path_for("get_compute_project", project_id=base_params["project_id"]))
#print(response.json.keys()) #print(response.json().keys())
#assert len(response.json.keys()) == 3 #assert len(response.json().keys()) == 3
assert response.json["project_id"] == base_params["project_id"] assert response.json()["project_id"] == base_params["project_id"]
assert response.json["name"] == base_params["name"] assert response.json()["name"] == base_params["name"]
assert response.json["variables"] is None assert response.json()["variables"] is None
@pytest.mark.asyncio async def test_show_project_invalid_uuid(app: FastAPI, client: AsyncClient) -> None:
async def test_show_project_invalid_uuid(compute_api):
response = await compute_api.get("/projects/50010203-0405-0607-0809-0a0b0c0d0e42") response = await client.get(app.url_path_for("get_compute_project",
assert response.status_code == 404 project_id="50010203-0405-0607-0809-0a0b0c0d0e42"))
assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_list_projects(app: FastAPI, client: AsyncClient) -> dict:
async def test_list_projects(compute_api):
ProjectManager.instance()._projects = {} ProjectManager.instance()._projects = {}
params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"} params = {"name": "test", "project_id": "51010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await compute_api.post("/projects", params) response = await client.post(app.url_path_for("create_compute_project"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"} params = {"name": "test", "project_id": "52010203-0405-0607-0809-0a0b0c0d0e0b"}
response = await compute_api.post("/projects", params) response = await client.post(app.url_path_for("create_compute_project"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await compute_api.get("/projects") response = await client.get(app.url_path_for("get_compute_projects"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert len(response.json) == 2 assert len(response.json()) == 2
assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json] assert "51010203-0405-0607-0809-0a0b0c0d0e0f" in [p["project_id"] for p in response.json()]
@pytest.mark.asyncio async def test_delete_project(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_delete_project(compute_api, compute_project):
with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock: with asyncio_patch("gns3server.compute.project.Project.delete", return_value=True) as mock:
response = await compute_api.delete("/projects/{project_id}".format(project_id=compute_project.id)) response = await client.delete(app.url_path_for("delete_compute_project", project_id=compute_project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
@pytest.mark.asyncio async def test_update_project(app: FastAPI, client: AsyncClient, base_params: dict) -> None:
async def test_update_project(compute_api, base_params):
response = await compute_api.post("/projects", base_params) response = await client.post(app.url_path_for("create_compute_project"), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
params = {"variables": [{"name": "TEST1", "value": "VAL1"}]} params = {"variables": [{"name": "TEST1", "value": "VAL1"}]}
response = await compute_api.put("/projects/{project_id}".format(project_id=base_params["project_id"]), params) response = await client.put(app.url_path_for("update_compute_project", project_id=base_params["project_id"]),
assert response.status_code == 200 json=params)
assert response.json["variables"] == [{"name": "TEST1", "value": "VAL1"}] assert response.status_code == status.HTTP_200_OK
assert response.json()["variables"] == [{"name": "TEST1", "value": "VAL1"}]
@pytest.mark.asyncio async def test_delete_project_invalid_uuid(app: FastAPI, client: AsyncClient) -> None:
async def test_delete_project_invalid_uuid(compute_api):
response = await compute_api.delete("/projects/{project_id}".format(project_id=uuid.uuid4())) response = await client.delete(app.url_path_for("delete_compute_project", project_id=str(uuid.uuid4())))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_close_project(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_close_project(compute_api, compute_project):
with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/close".format(project_id=compute_project.id)) response = await client.post(app.url_path_for("close_compute_project", project_id=compute_project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
@ -148,20 +147,18 @@ async def test_close_project(compute_api, compute_project):
# #
# ProjectHandler._notifications_listening = {compute_project.id: 2} # ProjectHandler._notifications_listening = {compute_project.id: 2}
# with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock: # with asyncio_patch("gns3server.compute.project.Project.close", return_value=True) as mock:
# response = await compute_api.post("/projects/{project_id}/close".format(project_id=compute_project.id)) # response = await client.post("/projects/{project_id}/close".format(project_id=compute_project.id))
# assert response.status_code == 204 # assert response.status_code == status.HTTP_204_NO_CONTENT
# assert not mock.called # assert not mock.called
@pytest.mark.asyncio async def test_close_project_invalid_uuid(app: FastAPI, client: AsyncClient) -> None:
async def test_close_project_invalid_uuid(compute_api):
response = await compute_api.post("/projects/{project_id}/close".format(project_id=uuid.uuid4())) response = await client.post(app.url_path_for("close_compute_project", project_id=str(uuid.uuid4())))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_get_file(app: FastAPI, client: AsyncClient, tmpdir) -> None:
async def test_get_file(compute_api, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}): with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
@ -169,48 +166,33 @@ async def test_get_file(compute_api, tmpdir):
with open(os.path.join(project.path, "hello"), "w+") as f: with open(os.path.join(project.path, "hello"), "w+") as f:
f.write("world") f.write("world")
response = await compute_api.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True) response = await client.get(app.url_path_for("get_compute_project_file", project_id=project.id, file_path="hello"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.content == b"world" assert response.content == b"world"
response = await compute_api.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True) response = await client.get(app.url_path_for("get_compute_project_file", project_id=project.id, file_path="false"))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
response = await compute_api.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await client.get(app.url_path_for("get_compute_project_file",
assert response.status_code == 404 project_id=project.id,
file_path="../hello"))
assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_write_file(app: FastAPI, client: AsyncClient, tmpdir) -> None:
async def test_write_file(compute_api, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}): with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b") project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
response = await compute_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True) response = await client.post(app.url_path_for("write_compute_project_file",
assert response.status_code == 204 project_id=project.id,
file_path="hello"), content=b"world")
assert response.status_code == status.HTTP_204_NO_CONTENT
with open(os.path.join(project.path, "hello")) as f: with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world" assert f.read() == "world"
response = await compute_api.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await client.post(app.url_path_for("write_compute_project_file",
assert response.status_code == 404 project_id=project.id,
file_path="../hello"))
assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio
async def test_stream_file(compute_api, tmpdir):
with patch("gns3server.config.Config.get_section_config", return_value={"projects_path": str(tmpdir)}):
project = ProjectManager.instance().create_project(project_id="01010203-0405-0607-0809-0a0b0c0d0e0b")
with open(os.path.join(project.path, "hello"), "w+") as f:
f.write("world")
response = await compute_api.get("/projects/{project_id}/files/hello".format(project_id=project.id), raw=True)
assert response.status_code == 200
assert response.content == b"world"
response = await compute_api.get("/projects/{project_id}/files/false".format(project_id=project.id), raw=True)
assert response.status_code == 404
response = await compute_api.get("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True)
assert response.status_code == 404

View File

@ -16,16 +16,22 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import uuid
import os import os
import sys import sys
import stat import stat
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture @pytest.fixture
def fake_qemu_bin(monkeypatch, tmpdir): def fake_qemu_bin(monkeypatch, tmpdir) -> str:
monkeypatch.setenv("PATH", str(tmpdir)) monkeypatch.setenv("PATH", str(tmpdir))
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
@ -40,7 +46,7 @@ def fake_qemu_bin(monkeypatch, tmpdir):
@pytest.fixture @pytest.fixture
def fake_qemu_vm(images_dir): def fake_qemu_vm(images_dir) -> str:
img_dir = os.path.join(images_dir, "QEMU") img_dir = os.path.join(images_dir, "QEMU")
bin_path = os.path.join(img_dir, "linux载.img") bin_path = os.path.join(img_dir, "linux载.img")
@ -51,140 +57,165 @@ def fake_qemu_vm(images_dir):
@pytest.fixture @pytest.fixture
def base_params(tmpdir, fake_qemu_bin): def base_params(tmpdir, fake_qemu_bin) -> dict:
"""Return standard parameters""" """Return standard parameters"""
return {"name": "PC TEST 1", "qemu_path": fake_qemu_bin} return {"name": "PC TEST 1", "qemu_path": fake_qemu_bin}
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, base_params: dict) -> None:
async def vm(compute_api, compute_project, base_params):
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
return response.json return response.json()
@pytest.mark.asyncio async def test_qemu_create(app: FastAPI,
async def test_qemu_create(compute_api, compute_project, base_params, fake_qemu_bin): client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_bin: str) -> None:
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["qemu_path"] == fake_qemu_bin assert response.json()["qemu_path"] == fake_qemu_bin
assert response.json["platform"] == "x86_64" assert response.json()["platform"] == "x86_64"
@pytest.mark.asyncio async def test_qemu_create_platform(app: FastAPI,
async def test_qemu_create_platform(compute_api, compute_project, base_params, fake_qemu_bin): client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_bin: str):
base_params["qemu_path"] = None base_params["qemu_path"] = None
base_params["platform"] = "x86_64" base_params["platform"] = "x86_64"
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=base_params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["qemu_path"] == fake_qemu_bin assert response.json()["qemu_path"] == fake_qemu_bin
assert response.json["platform"] == "x86_64" assert response.json()["platform"] == "x86_64"
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_qemu_create_with_params(compute_api, compute_project, base_params, fake_qemu_vm): async def test_qemu_create_with_params(app: FastAPI,
client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_vm: str):
params = base_params params = base_params
params["ram"] = 1024 params["ram"] = 1024
params["hda_disk_image"] = "linux载.img" params["hda_disk_image"] = "linux载.img"
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["ram"] == 1024 assert response.json()["ram"] == 1024
assert response.json["hda_disk_image"] == "linux载.img" assert response.json()["hda_disk_image"] == "linux载.img"
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") @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
@pytest.mark.asyncio async def test_qemu_create_with_project_file(app: FastAPI,
async def test_qemu_create_with_project_file(compute_api, compute_project, base_params, fake_qemu_vm): client: AsyncClient,
compute_project: Project,
base_params: dict,
fake_qemu_vm: str) -> None:
response = await compute_api.post("/projects/{project_id}/files/hello.img".format(project_id=compute_project.id), body=b"world", raw=True) response = await client.post(app.url_path_for("write_compute_project_file",
assert response.status_code == 204 project_id=compute_project.id,
file_path="hello.img"), content=b"world")
assert response.status_code == status.HTTP_204_NO_CONTENT
params = base_params params = base_params
params["hda_disk_image"] = "hello.img" params["hda_disk_image"] = "hello.img"
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_qemu_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["hda_disk_image"] == "hello.img" assert response.json()["hda_disk_image"] == "hello.img"
assert response.json["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7" assert response.json()["hda_disk_image_md5sum"] == "7d793037a0760186574b0282f2f435e7"
@pytest.mark.asyncio async def test_qemu_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict):
async def test_qemu_get(compute_api, compute_project, vm):
response = await compute_api.get("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.get(app.url_path_for("get_qemu_node", project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["node_directory"] == os.path.join(compute_project.path, "project-files", "qemu", vm["node_id"]) assert response.json()["node_directory"] == os.path.join(compute_project.path,
"project-files",
"qemu",
vm["node_id"])
@pytest.mark.asyncio async def test_qemu_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_start(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("start_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_qemu_stop(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("stop_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_qemu_reload(app: FastAPI, client: AsyncClient, vm) -> None:
async def test_qemu_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.reload", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("reload_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_qemu_suspend(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_suspend(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.suspend", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.suspend", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("suspend_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_qemu_resume(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_resume(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.resume", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("resume_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_qemu_delete(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.qemu.Qemu.delete_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.qemu.Qemu.delete_node", return_value=True) as mock:
response = await compute_api.delete("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(app.url_path_for("delete_qemu_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_qemu_update(app: FastAPI,
async def test_qemu_update(compute_api, vm, free_console_port, fake_qemu_vm): client: AsyncClient,
vm: dict,
free_console_port: int,
fake_qemu_vm: str) -> None:
params = { params = {
"name": "test", "name": "test",
@ -193,16 +224,17 @@ async def test_qemu_update(compute_api, vm, free_console_port, fake_qemu_vm):
"hdb_disk_image": "linux载.img" "hdb_disk_image": "linux载.img"
} }
response = await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.put(app.url_path_for("update_qemu_node",
assert response.status_code == 200 project_id=vm["project_id"],
assert response.json["name"] == "test" node_id=vm["node_id"]), json=params)
assert response.json["console"] == free_console_port assert response.status_code == status.HTTP_200_OK
assert response.json["hdb_disk_image"] == "linux载.img" assert response.json()["name"] == "test"
assert response.json["ram"] == 1024 assert response.json()["console"] == free_console_port
assert response.json()["hdb_disk_image"] == "linux载.img"
assert response.json()["ram"] == 1024
@pytest.mark.asyncio async def test_qemu_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_nio_create_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -212,14 +244,21 @@ async def test_qemu_nio_create_udp(compute_api, vm):
} }
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.add_ubridge_udp_connection"):
await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) await client.put(app.url_path_for("update_qemu_node",
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) project_id=vm["project_id"],
assert response.status_code == 201 node_id=vm["node_id"]), json={"adapters": 2})
assert response.json["type"] == "nio_udp"
url = app.url_path_for("create_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.post(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_qemu_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_nio_update_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -228,17 +267,31 @@ async def test_qemu_nio_update_udp(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) await client.put(app.url_path_for("update_qemu_node",
await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) project_id=vm["project_id"],
node_id=vm["node_id"]), json={"adapters": 2})
url = app.url_path_for("create_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
await client.post(url, json=params)
params["filters"] = {} params["filters"] = {}
response = await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
assert response.status_code == 201, response.body.decode() url = app.url_path_for("update_qemu_node_nio",
assert response.json["type"] == "nio_udp" project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.put(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_qemu_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_delete_nio(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -248,57 +301,69 @@ async def test_qemu_delete_nio(compute_api, vm):
} }
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"): with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._ubridge_send"):
await compute_api.put("/projects/{project_id}/qemu/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), {"adapters": 2}) await client.put(app.url_path_for("update_qemu_node",
await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) project_id=vm["project_id"],
response = await compute_api.delete("/projects/{project_id}/qemu/nodes/{node_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) node_id=vm["node_id"]), json={"adapters": 2})
assert response.status_code == 204
url = app.url_path_for("create_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
await client.post(url, json=params)
url = app.url_path_for("delete_qemu_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="1",
port_number="0")
response = await client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_qemu_list_binaries(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_list_binaries(compute_api, vm):
ret = [{"path": "/tmp/1", "version": "2.2.0"}, ret = [{"path": "/tmp/1", "version": "2.2.0"},
{"path": "/tmp/2", "version": "2.1.0"}] {"path": "/tmp/2", "version": "2.1.0"}]
with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock: with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock:
response = await compute_api.get("/qemu/binaries".format(project_id=vm["project_id"])) response = await client.get(app.url_path_for("get_qemu_binaries"))
assert mock.called_with(None) assert mock.called_with(None)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == ret assert response.json() == ret
@pytest.mark.asyncio # async def test_qemu_list_binaries_filter(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_qemu_list_binaries_filter(compute_api, vm): #
# ret = [
ret = [ # {"path": "/tmp/x86_64", "version": "2.2.0"},
{"path": "/tmp/x86_64", "version": "2.2.0"}, # {"path": "/tmp/alpha", "version": "2.1.0"},
{"path": "/tmp/alpha", "version": "2.1.0"}, # {"path": "/tmp/i386", "version": "2.1.0"}
{"path": "/tmp/i386", "version": "2.1.0"} # ]
] #
# with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock:
with asyncio_patch("gns3server.compute.qemu.Qemu.binary_list", return_value=ret) as mock: # response = await client.get(app.url_path_for("get_qemu_binaries"),
response = await compute_api.get("/qemu/binaries".format(project_id=vm["project_id"]), body={"archs": ["i386"]}) # json={"archs": ["i386"]})
assert response.status_code == 200 # assert response.status_code == status.HTTP_200_OK
assert mock.called_with(["i386"]) # assert mock.called_with(["i386"])
assert response.json == ret # assert response.json() == ret
@pytest.mark.asyncio async def test_images(app: FastAPI, client: AsyncClient, fake_qemu_vm) -> None:
async def test_images(compute_api, fake_qemu_vm):
response = await compute_api.get("/qemu/images") response = await client.get(app.url_path_for("get_qemu_images"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert {"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} in response.json assert {"filename": "linux载.img", "path": "linux载.img", "md5sum": "c4ca4238a0b923820dcc509a6f75849b", "filesize": 1} in response.json()
@pytest.mark.asyncio async def test_upload_image(app: FastAPI, client: AsyncClient, tmpdir: str) -> None:
async def test_upload_image(compute_api, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/qemu/images/test2使", body="TEST", raw=True)
assert response.status_code == 204
print(os.listdir(tmpdir)) response = await client.post(app.url_path_for("upload_qemu_image",
filename="test2使"), content=b"TEST")
assert response.status_code == status.HTTP_204_NO_CONTENT
with open(str(tmpdir / "test2使")) as f: with open(str(tmpdir / "test2使")) as f:
assert f.read() == "TEST" assert f.read() == "TEST"
@ -307,12 +372,13 @@ async def test_upload_image(compute_api, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
@pytest.mark.asyncio async def test_upload_image_ova(app: FastAPI, client: AsyncClient, tmpdir:str) -> None:
async def test_upload_image_ova(compute_api, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/qemu/images/test2.ova/test2.vmdk", body="TEST", raw=True)
assert response.status_code == 204 response = await client.post(app.url_path_for("upload_qemu_image",
filename="test2.ova/test2.vmdk"), content=b"TEST")
assert response.status_code == status.HTTP_204_NO_CONTENT
with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f: with open(str(tmpdir / "test2.ova" / "test2.vmdk")) as f:
assert f.read() == "TEST" assert f.read() == "TEST"
@ -322,28 +388,27 @@ async def test_upload_image_ova(compute_api, tmpdir):
assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf" assert checksum == "033bd94b1168d7e4f0d644c3c95e35bf"
@pytest.mark.asyncio async def test_upload_image_forbiden_location(app: FastAPI, client: AsyncClient, tmpdir: str) -> None:
async def test_upload_image_forbiden_location(compute_api, tmpdir):
with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)): with patch("gns3server.compute.Qemu.get_images_directory", return_value=str(tmpdir)):
response = await compute_api.post("/qemu/images/../../test2", body="TEST", raw=True) response = await client.post(app.url_path_for("upload_qemu_image",
assert response.status_code == 404 filename="/qemu/images/../../test2"), content=b"TEST")
assert response.status_code == status.HTTP_403_FORBIDDEN
@pytest.mark.skipif(not sys.platform.startswith("win") and os.getuid() == 0, reason="Root can delete any image") @pytest.mark.skipif(not sys.platform.startswith("win") and os.getuid() == 0, reason="Root can delete any image")
@pytest.mark.asyncio async def test_upload_image_permission_denied(app: FastAPI, client: AsyncClient, images_dir: str) -> None:
async def test_upload_image_permission_denied(compute_api, images_dir):
with open(os.path.join(images_dir, "QEMU", "test2.tmp"), "w+") as f: with open(os.path.join(images_dir, "QEMU", "test2.tmp"), "w+") as f:
f.write("") f.write("")
os.chmod(os.path.join(images_dir, "QEMU", "test2.tmp"), 0) os.chmod(os.path.join(images_dir, "QEMU", "test2.tmp"), 0)
response = await compute_api.post("/qemu/images/test2", body="TEST", raw=True) response = await client.post(app.url_path_for("upload_qemu_image", filename="test2"), content=b"TEST")
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_create_img_relative(compute_api): async def test_create_img_relative(app: FastAPI, client: AsyncClient):
params = { params = {
"qemu_img": "/tmp/qemu-img", "qemu_img": "/tmp/qemu-img",
@ -356,12 +421,11 @@ async def test_create_img_relative(compute_api):
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = await compute_api.post("/qemu/img", params) response = await client.post(app.url_path_for("create_qemu_image"), json=params)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_create_img_absolute_non_local(app: FastAPI, client: AsyncClient, config: dict) -> None:
async def test_create_img_absolute_non_local(compute_api, config):
config.set("Server", "local", "false") config.set("Server", "local", "false")
params = { params = {
@ -375,12 +439,11 @@ async def test_create_img_absolute_non_local(compute_api, config):
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = await compute_api.post("/qemu/img", params) response = await client.post(app.url_path_for("create_qemu_image"), json=params)
assert response.status_code == 403 assert response.status_code == 403
@pytest.mark.asyncio async def test_create_img_absolute_local(app: FastAPI, client: AsyncClient, config: dict) -> None:
async def test_create_img_absolute_local(compute_api, config):
config.set("Server", "local", "true") config.set("Server", "local", "true")
params = { params = {
@ -394,60 +457,76 @@ async def test_create_img_absolute_local(compute_api, config):
"size": 100 "size": 100
} }
with asyncio_patch("gns3server.compute.Qemu.create_disk"): with asyncio_patch("gns3server.compute.Qemu.create_disk"):
response = await compute_api.post("/qemu/img", params) response = await client.post(app.url_path_for("create_qemu_image"), json=params)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_capabilities(app: FastAPI, client: AsyncClient) -> None:
async def test_capabilities(compute_api):
with asyncio_patch("gns3server.compute.Qemu.get_kvm_archs", return_value=["x86_64"]): with asyncio_patch("gns3server.compute.Qemu.get_kvm_archs", return_value=["x86_64"]):
response = await compute_api.get("/qemu/capabilities") response = await client.get(app.url_path_for("get_qemu_capabilities"))
assert response.json["kvm"] == ["x86_64"] assert response.json()["kvm"] == ["x86_64"]
@pytest.mark.asyncio async def test_qemu_duplicate(app: FastAPI,
async def test_qemu_duplicate(compute_api, compute_project, vm, base_params): client: AsyncClient,
compute_project: Project,
vm: dict,
base_params: dict) -> None:
# create destination node first # create destination node first
response = await compute_api.post("/projects/{project_id}/qemu/nodes".format(project_id=compute_project.id), base_params) response = await client.post(app.url_path_for("create_qemu_node",
assert response.status_code == 201 project_id=vm["project_id"]), json=base_params)
params = {"destination_node_id": response.json["node_id"]} assert response.status_code == status.HTTP_201_CREATED
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) params = {"destination_node_id": response.json()["node_id"]}
assert response.status_code == 201 response = await client.post(app.url_path_for("duplicate_qemu_node",
project_id=vm["project_id"], node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_qemu_start_capture(compute_api, vm): async def test_qemu_start_capture(app: FastAPI, client: AsyncClient, vm):
params = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "data_link_type": "DLT_EN10MB"
} }
url = app.url_path_for("start_qemu_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start_capture") as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_qemu_stop_capture(compute_api, vm): async def test_qemu_stop_capture(app: FastAPI, client: AsyncClient, vm):
url = app.url_path_for("stop_qemu_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True): with patch("gns3server.compute.qemu.qemu_vm.QemuVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop_capture") as mock: with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @pytest.mark.asyncio
# async def test_qemu_pcap(compute_api, vm, compute_project): # async def test_qemu_pcap(app: FastAPI, client: AsyncClient, vm, compute_project):
# #
# with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.get_nio"): # with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM.get_nio"):
# with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"): # with asyncio_patch("gns3server.compute.qemu.Qemu.stream_pcap_file"):
# response = await compute_api.get("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) # response = await client.get("/projects/{project_id}/qemu/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
# assert response.status_code == 200 # assert response.status_code == status.HTTP_200_OK

View File

@ -16,13 +16,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def vm(compute_api, compute_project):
vboxmanage_path = "/fake/VboxManage" vboxmanage_path = "/fake/VboxManage"
params = { params = {
@ -32,16 +38,16 @@ async def vm(compute_api, compute_project):
} }
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_virtualbox_node", project_id=compute_project.id),
json=params)
assert mock.called assert mock.called
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path): with patch("gns3server.compute.virtualbox.VirtualBox.find_vboxmanage", return_value=vboxmanage_path):
return response.json return response.json()
@pytest.mark.asyncio async def test_vbox_create(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_vbox_create(compute_api, compute_project):
params = { params = {
"name": "VM1", "name": "VM1",
@ -50,68 +56,76 @@ async def test_vbox_create(compute_api, compute_project):
} }
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True): with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.create", return_value=True):
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_virtualbox_node", project_id=compute_project.id),
assert response.status_code == 201 json=params)
assert response.json["name"] == "VM1" assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == compute_project.id assert response.json()["name"] == "VM1"
assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_vbox_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_vbox_get(compute_api, compute_project, vm):
response = await compute_api.get("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.get(app.url_path_for("get_virtualbox_node",
assert response.status_code == 200 project_id=vm["project_id"],
assert response.json["name"] == "VMTEST" node_id=vm["node_id"]))
assert response.json["project_id"] == compute_project.id assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "VMTEST"
assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_vbox_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vbox_start(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.check_hw_virtualization", return_value=True): with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.check_hw_virtualization", return_value=True):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"]))
response = await client.post(app.url_path_for("start_virtualbox_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vbox_stop(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vbox_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("stop_virtualbox_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vbox_suspend(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vbox_suspend(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.suspend", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("suspend_virtualbox_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vbox_resume(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vbox_resume(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.resume", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("resume_virtualbox_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vbox_reload(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vbox_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.reload", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("reload_virtualbox_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vbox_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vbox_nio_create_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -120,18 +134,24 @@ async def test_vbox_nio_create_udp(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
url = app.url_path_for("create_virtualbox_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock: with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_add_nio_binding') as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["type"] == "nio_udp" assert response.json()["type"] == "nio_udp"
# @pytest.mark.asyncio # @pytest.mark.asyncio
# async def test_vbox_nio_update_udp(compute_api, vm): # async def test_vbox_nio_update_udp(app: FastAPI, client: AsyncClient, vm):
# #
# params = { # params = {
# "type": "nio_udp", # "type": "nio_udp",
@ -143,68 +163,86 @@ async def test_vbox_nio_create_udp(compute_api, vm):
# #
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'): # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.ethernet_adapters'):
# with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'): # with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding'):
# response = await compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) # response = await client.put("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
# #
# assert response.status_code == 201 # assert response.status_code == status.HTTP_201_CREATED
# assert response.json["type"] == "nio_udp" # assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_vbox_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vbox_delete_nio(compute_api, vm):
url = app.url_path_for("delete_virtualbox_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock: with asyncio_patch('gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.adapter_remove_nio_binding') as mock:
response = await compute_api.delete("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(url)
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
assert response.status_code == status.HTTP_204_NO_CONTENT
assert response.status_code == 204
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_vbox_update(compute_api, vm, free_console_port): async def test_vbox_update(app: FastAPI, client: AsyncClient, vm, free_console_port):
params = { params = {
"name": "test", "name": "test",
"console": free_console_port "console": free_console_port
} }
response = await compute_api.put("/projects/{project_id}/virtualbox/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.put(app.url_path_for("update_virtualbox_node",
assert response.status_code == 200 project_id=vm["project_id"],
assert response.json["name"] == "test" node_id=vm["node_id"]), json=params)
assert response.json["console"] == free_console_port assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test"
assert response.json()["console"] == free_console_port
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_virtualbox_start_capture(compute_api, vm): async def test_virtualbox_start_capture(app: FastAPI, client: AsyncClient, vm):
params = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "data_link_type": "DLT_EN10MB"
} }
url = app.url_path_for("start_virtualbox_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_virtualbox_stop_capture(compute_api, vm): async def test_virtualbox_stop_capture(app: FastAPI, client: AsyncClient, vm):
url = app.url_path_for("stop_virtualbox_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True): with patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as mock: with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @pytest.mark.asyncio
# async def test_virtualbox_pcap(compute_api, vm, compute_project): # async def test_virtualbox_pcap(app: FastAPI, client: AsyncClient, vm, compute_project):
# #
# with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"): # with asyncio_patch("gns3server.compute.virtualbox.virtualbox_vm.VirtualBoxVM.get_nio"):
# with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"): # with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.stream_pcap_file"):
# response = await compute_api.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) # response = await client.get("/projects/{project_id}/virtualbox/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
# assert response.status_code == 200 # assert response.status_code == status.HTTP_200_OK

View File

@ -16,13 +16,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project, vmx_path: str) -> dict:
async def vm(compute_api, compute_project, vmx_path):
params = { params = {
"name": "VMTEST", "name": "VMTEST",
@ -31,15 +37,15 @@ async def vm(compute_api, compute_project, vmx_path):
} }
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_vmware_node", project_id=compute_project.id),
assert mock.called json=params)
assert response.status_code == 201, response.body.decode() assert mock.called
return response.json assert response.status_code == status.HTTP_201_CREATED
return response.json()
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio def vmx_path(tmpdir: str) -> str:
def vmx_path(tmpdir):
""" """
Return a fake VMX file Return a fake VMX file
""" """
@ -50,8 +56,7 @@ def vmx_path(tmpdir):
return path return path
@pytest.mark.asyncio async def test_vmware_create(app: FastAPI, client: AsyncClient, compute_project: Project, vmx_path: str) -> None:
async def test_vmware_create(compute_api, compute_project, vmx_path):
params = { params = {
"name": "VM1", "name": "VM1",
@ -60,70 +65,74 @@ async def test_vmware_create(compute_api, compute_project, vmx_path):
} }
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True): with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.create", return_value=True):
response = await compute_api.post("/projects/{project_id}/vmware/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_vmware_node", project_id=compute_project.id),
assert response.status_code == 201, response.body.decode() json=params)
assert response.json["name"] == "VM1" assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == compute_project.id assert response.json()["name"] == "VM1"
assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_vmware_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_vmware_get(compute_api, compute_project, vm):
response = await compute_api.get("/projects/{project_id}/vmware/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.get(app.url_path_for("get_vmware_node", project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "VMTEST" assert response.json()["name"] == "VMTEST"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_vmware_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_start(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.check_hw_virtualization", return_value=True) as mock1: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.check_hw_virtualization", return_value=True) as mock1:
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock2: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start", return_value=True) as mock2:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("start_vmware_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock1.called assert mock1.called
assert mock2.called assert mock2.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vmware_stop(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("stop_vmware_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vmware_suspend(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_suspend(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.suspend", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/suspend".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("suspend_vmware_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vmware_resume(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_resume(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.resume", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/resume".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("resume_vmware_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vmware_reload(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.reload", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("reload_vmware_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vmware_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_nio_create_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -132,18 +141,24 @@ async def test_vmware_nio_create_udp(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
url = app.url_path_for("create_vmware_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock: with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_add_nio_binding') as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["type"] == "nio_udp" assert response.json()["type"] == "nio_udp"
# @pytest.mark.asyncio # @pytest.mark.asyncio
# async def test_vmware_nio_update_udp(compute_api, vm): # async def test_vmware_nio_update_udp(app: FastAPI, client: AsyncClient, vm):
# #
# params = { # params = {
# "type": "nio_udp", # "type": "nio_udp",
@ -156,68 +171,84 @@ async def test_vmware_nio_create_udp(compute_api, vm):
# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'): # with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM._ubridge_send'):
# with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'): # with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.ethernet_adapters'):
# with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock: # with patch('gns3server.compute.vmware.vmware_vm.VMwareVM._get_vnet') as mock:
# response = await compute_api.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) # response = await client.put("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params)
# assert response.status_code == 201 # assert response.status_code == status.HTTP_201_CREATED
# assert response.json["type"] == "nio_udp" # assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_vmware_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_delete_nio(compute_api, vm):
url = app.url_path_for("delete_vmware_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock: with asyncio_patch('gns3server.compute.vmware.vmware_vm.VMwareVM.adapter_remove_nio_binding') as mock:
response = await compute_api.delete("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(url)
assert mock.called assert mock.called
args, kwgars = mock.call_args args, kwgars = mock.call_args
assert args[0] == 0 assert args[0] == 0
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vmware_update(app: FastAPI, client: AsyncClient, vm: dict, free_console_port: int) -> None:
async def test_vmware_update(compute_api, vm, free_console_port):
params = { params = {
"name": "test", "name": "test",
"console": free_console_port "console": free_console_port
} }
response = await compute_api.put("/projects/{project_id}/vmware/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.put(app.url_path_for("update_vmware_node",
assert response.status_code == 200 project_id=vm["project_id"],
assert response.json["name"] == "test" node_id=vm["node_id"]), json=params)
assert response.json["console"] == free_console_port assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test"
assert response.json()["console"] == free_console_port
@pytest.mark.asyncio async def test_vmware_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_start_capture(compute_api, vm):
params = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "data_link_type": "DLT_EN10MB"
} }
url = app.url_path_for("start_vmware_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params) response = await client.post(url, json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio async def test_vmware_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vmware_stop_capture(compute_api, vm):
url = app.url_path_for("stop_vmware_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True): with patch("gns3server.compute.vmware.vmware_vm.VMwareVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as mock: with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @pytest.mark.asyncio
# async def test_vmware_pcap(compute_api, vm, compute_project): # async def test_vmware_pcap(app: FastAPI, client: AsyncClient, vm, compute_project):
# #
# with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"): # with asyncio_patch("gns3server.compute.vmware.vmware_vm.VMwareVM.get_nio"):
# with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"): # with asyncio_patch("gns3server.compute.vmware.VMware.stream_pcap_file"):
# response = await compute_api.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) # response = await client.get("/projects/{project_id}/vmware/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
# assert response.status_code == 200 # assert response.status_code == status.HTTP_200_OK

View File

@ -16,72 +16,75 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
import uuid
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from unittest.mock import patch from unittest.mock import patch
from gns3server.compute.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def vm(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def vm(compute_api, compute_project):
params = {"name": "PC TEST 1"} params = {"name": "PC TEST 1"}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
return response.json return response.json()
@pytest.mark.asyncio async def test_vpcs_create(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_vpcs_create(compute_api, compute_project):
params = {"name": "PC TEST 1"} params = {"name": "PC TEST 1"}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_vpcs_get(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_vpcs_get(compute_api, compute_project, vm):
response = await compute_api.get("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.get(app.url_path_for("get_vpcs_node", project_id=vm["project_id"], node_id=vm["node_id"]))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["status"] == "stopped" assert response.json()["status"] == "stopped"
@pytest.mark.asyncio async def test_vpcs_create_startup_script(app: FastAPI, client: AsyncClient, compute_project: Project) -> None:
async def test_vpcs_create_startup_script(compute_api, compute_project):
params = { params = {
"name": "PC TEST 1", "name": "PC TEST 1",
"startup_script": "ip 192.168.1.2\necho TEST" "startup_script": "ip 192.168.1.2\necho TEST"
} }
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
@pytest.mark.asyncio async def test_vpcs_create_port(app: FastAPI,
async def test_vpcs_create_port(compute_api, compute_project, free_console_port): client: AsyncClient,
compute_project: Project,
free_console_port: int) -> None:
params = { params = {
"name": "PC TEST 1", "name": "PC TEST 1",
"console": free_console_port "console": free_console_port
} }
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "PC TEST 1" assert response.json()["name"] == "PC TEST 1"
assert response.json["project_id"] == compute_project.id assert response.json()["project_id"] == compute_project.id
assert response.json["console"] == free_console_port assert response.json()["console"] == free_console_port
@pytest.mark.asyncio async def test_vpcs_nio_create_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_nio_create_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -90,14 +93,19 @@ async def test_vpcs_nio_create_udp(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
url = app.url_path_for("create_vpcs_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["type"] == "nio_udp" assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_vpcs_nio_update_udp(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_nio_update_udp(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -106,18 +114,28 @@ async def test_vpcs_nio_update_udp(compute_api, vm):
"rhost": "127.0.0.1" "rhost": "127.0.0.1"
} }
url = app.url_path_for("create_vpcs_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.add_ubridge_udp_connection"):
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(url, json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
params["filters"] = {} params["filters"] = {}
response = await compute_api.put("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("update_vpcs_node_nio",
assert response.status_code == 201, response.body.decode("utf-8") project_id=vm["project_id"],
assert response.json["type"] == "nio_udp" node_id=vm["node_id"],
adapter_number="0",
port_number="0")
response = await client.put(url, json=params)
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["type"] == "nio_udp"
@pytest.mark.asyncio async def test_vpcs_delete_nio(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_delete_nio(compute_api, vm):
params = { params = {
"type": "nio_udp", "type": "nio_udp",
@ -127,63 +145,78 @@ async def test_vpcs_delete_nio(compute_api, vm):
} }
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"): with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM._ubridge_send"):
await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) url = app.url_path_for("create_vpcs_node_nio",
response = await compute_api.delete("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/nio".format(project_id=vm["project_id"], node_id=vm["node_id"])) project_id=vm["project_id"],
assert response.status_code == 204, response.body.decode() node_id=vm["node_id"],
adapter_number="0",
port_number="0")
await client.post(url, json=params)
url = app.url_path_for("delete_vpcs_node_nio",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
response = await client.delete(url)
assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vpcs_start(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_start(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/start".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("start_vpcs_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vpcs_stop(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_stop(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("stop_vpcs_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vpcs_reload(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_reload(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.reload", return_value=True) as mock:
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/reload".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(app.url_path_for("reload_vpcs_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vpcs_delete(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_delete(compute_api, vm):
with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock: with asyncio_patch("gns3server.compute.vpcs.VPCS.delete_node", return_value=True) as mock:
response = await compute_api.delete("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.delete(app.url_path_for("delete_vpcs_node",
project_id=vm["project_id"],
node_id=vm["node_id"]))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_vpcs_duplicate(app: FastAPI, client: AsyncClient, compute_project: Project, vm: dict) -> None:
async def test_vpcs_duplicate(compute_api, compute_project, vm):
# create destination node first # create destination node first
params = {"name": "PC TEST 1"} params = {"name": "PC TEST 1"}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes".format(project_id=compute_project.id), params) response = await client.post(app.url_path_for("create_vpcs_node", project_id=compute_project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
params = {"destination_node_id": response.json["node_id"]} params = {"destination_node_id": response.json()["node_id"]}
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/duplicate".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.post(app.url_path_for("duplicate_vpcs_node",
assert response.status_code == 201 project_id=vm["project_id"],
node_id=vm["node_id"]), json=params)
assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.asyncio async def test_vpcs_update(app: FastAPI, client: AsyncClient, vm: dict, free_console_port: int) -> None:
async def test_vpcs_update(compute_api, vm, free_console_port):
console_port = free_console_port console_port = free_console_port
params = { params = {
@ -191,42 +224,54 @@ async def test_vpcs_update(compute_api, vm, free_console_port):
"console": console_port "console": console_port
} }
response = await compute_api.put("/projects/{project_id}/vpcs/nodes/{node_id}".format(project_id=vm["project_id"], node_id=vm["node_id"]), params) response = await client.put(app.url_path_for("update_vpcs_node",
assert response.status_code == 200 project_id=vm["project_id"],
assert response.json["name"] == "test" node_id=vm["node_id"]), json=params)
assert response.json["console"] == console_port assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test"
assert response.json()["console"] == console_port
@pytest.mark.asyncio async def test_vpcs_start_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_start_capture(compute_api, vm):
params = { params = {
"capture_file_name": "test.pcap", "capture_file_name": "test.pcap",
"data_link_type": "DLT_EN10MB" "data_link_type": "DLT_EN10MB"
} }
url = app.url_path_for("start_vpcs_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.start_capture") as mock:
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/capture/start".format(project_id=vm["project_id"], node_id=vm["node_id"]), body=params) response = await client.post(url, json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert mock.called assert mock.called
assert "test.pcap" in response.json["pcap_file_path"] assert "test.pcap" in response.json()["pcap_file_path"]
@pytest.mark.asyncio async def test_vpcs_stop_capture(app: FastAPI, client: AsyncClient, vm: dict) -> None:
async def test_vpcs_stop_capture(compute_api, vm):
url = app.url_path_for("stop_vpcs_node_capture",
project_id=vm["project_id"],
node_id=vm["node_id"],
adapter_number="0",
port_number="0")
with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True): with patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.is_running", return_value=True):
with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as mock: with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.stop_capture") as mock:
response = await compute_api.post("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/capture/stop".format(project_id=vm["project_id"], node_id=vm["node_id"])) response = await client.post(url)
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
# @pytest.mark.asyncio # @pytest.mark.asyncio
# async def test_vpcs_pcap(compute_api, vm, compute_project): # async def test_vpcs_pcap(app: FastAPI, client: AsyncClient, vm, compute_project: Project):
# #
# with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"): # with asyncio_patch("gns3server.compute.vpcs.vpcs_vm.VPCSVM.get_nio"):
# with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"): # with asyncio_patch("gns3server.compute.vpcs.VPCS.stream_pcap_file"):
# response = await compute_api.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True) # response = await client.get("/projects/{project_id}/vpcs/nodes/{node_id}/adapters/0/ports/0/pcap".format(project_id=compute_project.id, node_id=vm["node_id"]), raw=True)
# assert response.status_code == 200 # assert response.status_code == status.HTTP_200_OK

View File

@ -17,10 +17,14 @@
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
@pytest.mark.asyncio pytestmark = pytest.mark.asyncio
async def test_appliances_list(controller_api):
response = await controller_api.get("/appliances/")
assert response.status_code == 200 async def test_appliances_list(app: FastAPI, client: AsyncClient) -> None:
assert len(response.json) > 0
response = await client.get(app.url_path_for("get_appliances"))
assert response.status_code == status.HTTP_200_OK
assert len(response.json()) > 0

View File

@ -16,11 +16,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.controller import Controller
pytestmark = pytest.mark.asyncio
import unittest import unittest
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
@pytest.mark.asyncio
async def test_compute_create_without_id(controller_api, controller): async def test_compute_create_without_id(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
params = { params = {
"protocol": "http", "protocol": "http",
@ -29,17 +37,17 @@ async def test_compute_create_without_id(controller_api, controller):
"user": "julien", "user": "julien",
"password": "secure"} "password": "secure"}
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("create_compute"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["user"] == "julien" response_content = response.json()
assert response.json["compute_id"] is not None assert response_content["user"] == "julien"
assert "password" not in response.json assert response_content["compute_id"] is not None
assert "password" not in response_content
assert len(controller.computes) == 1 assert len(controller.computes) == 1
assert controller.computes[response.json["compute_id"]].host == "localhost" assert controller.computes[response_content["compute_id"]].host == "localhost"
@pytest.mark.asyncio async def test_compute_create_with_id(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_compute_create_with_id(controller_api, controller):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -49,16 +57,15 @@ async def test_compute_create_with_id(controller_api, controller):
"user": "julien", "user": "julien",
"password": "secure"} "password": "secure"}
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("create_compute"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["user"] == "julien" assert response.json()["user"] == "julien"
assert "password" not in response.json assert "password" not in response.json()
assert len(controller.computes) == 1 assert len(controller.computes) == 1
assert controller.computes["my_compute_id"].host == "localhost" assert controller.computes["my_compute_id"].host == "localhost"
@pytest.mark.asyncio async def test_compute_get(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_compute_get(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -69,15 +76,14 @@ async def test_compute_get(controller_api):
"password": "secure" "password": "secure"
} }
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("create_compute"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await controller_api.get("/computes/my_compute_id") response = await client.get(app.url_path_for("update_compute", compute_id="my_compute_id"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
@pytest.mark.asyncio async def test_compute_update(app: FastAPI, client: AsyncClient) -> None:
async def test_compute_update(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -88,22 +94,20 @@ async def test_compute_update(controller_api):
"password": "secure" "password": "secure"
} }
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("create_compute"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await client.get(app.url_path_for("get_compute", compute_id="my_compute_id"))
response = await controller_api.get("/computes/my_compute_id") assert response.status_code == status.HTTP_200_OK
assert response.status_code == 200 assert response.json()["protocol"] == "http"
assert response.json["protocol"] == "http"
params["protocol"] = "https" params["protocol"] = "https"
response = await controller_api.put("/computes/my_compute_id", params) response = await client.put(app.url_path_for("update_compute", compute_id="my_compute_id"), json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["protocol"] == "https" assert response.json()["protocol"] == "https"
@pytest.mark.asyncio async def test_compute_list(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_compute_list(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -115,13 +119,13 @@ async def test_compute_list(controller_api):
"name": "My super server" "name": "My super server"
} }
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("create_compute"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["user"] == "julien" assert response.json()["user"] == "julien"
assert "password" not in response.json assert "password" not in response.json()
response = await controller_api.get("/computes") response = await client.get(app.url_path_for("get_computes"))
for compute in response.json: for compute in response.json():
if compute['compute_id'] != 'local': if compute['compute_id'] != 'local':
assert compute == { assert compute == {
'compute_id': 'my_compute_id', 'compute_id': 'my_compute_id',
@ -146,8 +150,7 @@ async def test_compute_list(controller_api):
} }
@pytest.mark.asyncio async def test_compute_delete(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_compute_delete(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -157,41 +160,40 @@ async def test_compute_delete(controller_api):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = await controller_api.post("/computes", params)
assert response.status_code == 201
response = await controller_api.get("/computes") response = await client.post(app.url_path_for("create_compute"), json=params)
assert len(response.json) == 1 assert response.status_code == status.HTTP_201_CREATED
response = await controller_api.delete("/computes/my_compute_id") response = await client.get(app.url_path_for("get_computes"))
assert response.status_code == 204 assert len(response.json()) == 1
response = await controller_api.get("/computes") response = await client.delete(app.url_path_for("delete_compute", compute_id="my_compute_id"))
assert len(response.json) == 0 assert response.status_code == status.HTTP_204_NO_CONTENT
response = await client.get(app.url_path_for("get_computes"))
assert len(response.json()) == 0
@pytest.mark.asyncio async def test_compute_list_images(app: FastAPI, client: AsyncClient) -> None:
async def test_compute_list_images(controller_api):
params = { params = {
"compute_id": "my_compute", "compute_id": "my_compute_id",
"protocol": "http", "protocol": "http",
"host": "localhost", "host": "localhost",
"port": 84, "port": 84,
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("create_compute"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
with asyncio_patch("gns3server.controller.compute.Compute.images", return_value=[{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]) as mock: with asyncio_patch("gns3server.controller.compute.Compute.images", return_value=[{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]) as mock:
response = await controller_api.get("/computes/my_compute/qemu/images") response = await client.get(app.url_path_for("delete_compute", compute_id="my_compute_id") + "/qemu/images")
assert response.json == [{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}] assert response.json() == [{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]
mock.assert_called_with("qemu") mock.assert_called_with("qemu")
@pytest.mark.asyncio async def test_compute_list_vms(app: FastAPI, client: AsyncClient) -> None:
async def test_compute_list_vms(controller_api):
params = { params = {
"compute_id": "my_compute", "compute_id": "my_compute",
@ -201,17 +203,16 @@ async def test_compute_list_vms(controller_api):
"user": "julien", "user": "julien",
"password": "secure" "password": "secure"
} }
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("get_computes"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock: with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock:
response = await controller_api.get("/computes/my_compute/virtualbox/vms") response = await client.get(app.url_path_for("get_compute", compute_id="my_compute_id") + "/virtualbox/vms")
mock.assert_called_with("GET", "virtualbox", "vms") mock.assert_called_with("GET", "virtualbox", "vms")
assert response.json == [] assert response.json() == []
@pytest.mark.asyncio async def test_compute_create_img(app: FastAPI, client: AsyncClient) -> None:
async def test_compute_create_img(controller_api):
params = { params = {
"compute_id": "my_compute", "compute_id": "my_compute",
@ -222,18 +223,17 @@ async def test_compute_create_img(controller_api):
"password": "secure" "password": "secure"
} }
response = await controller_api.post("/computes", params) response = await client.post(app.url_path_for("get_computes"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
params = {"path": "/test"} params = {"path": "/test"}
with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock: with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock:
response = await controller_api.post("/computes/my_compute/qemu/img", params) response = await client.post(app.url_path_for("get_compute", compute_id="my_compute_id") + "/qemu/img", json=params)
assert response.json == [] assert response.json() == []
mock.assert_called_with("POST", "qemu", "img", data=unittest.mock.ANY) mock.assert_called_with("POST", "qemu", "img", data=unittest.mock.ANY)
@pytest.mark.asyncio async def test_compute_autoidlepc(app: FastAPI, client: AsyncClient) -> None:
async def test_compute_autoidlepc(controller_api):
params = { params = {
"compute_id": "my_compute_id", "compute_id": "my_compute_id",
@ -244,7 +244,7 @@ async def test_compute_autoidlepc(controller_api):
"password": "secure" "password": "secure"
} }
await controller_api.post("/computes", params) await client.post(app.url_path_for("get_computes"), json=params)
params = { params = {
"platform": "c7200", "platform": "c7200",
@ -253,9 +253,9 @@ async def test_compute_autoidlepc(controller_api):
} }
with asyncio_patch("gns3server.controller.Controller.autoidlepc", return_value={"idlepc": "0x606de20c"}) as mock: with asyncio_patch("gns3server.controller.Controller.autoidlepc", return_value={"idlepc": "0x606de20c"}) as mock:
response = await controller_api.post("/computes/my_compute_id/auto_idlepc", params) response = await client.post(app.url_path_for("get_compute", compute_id="my_compute_id") + "/auto_idlepc", json=params)
assert mock.called assert mock.called
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
# FIXME # FIXME

View File

@ -18,25 +18,29 @@
import os import os
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from unittest.mock import MagicMock from unittest.mock import MagicMock
from gns3server.config import Config
@pytest.mark.asyncio pytestmark = pytest.mark.asyncio
async def test_shutdown_local(controller_api, config):
async def test_shutdown_local(app: FastAPI, client: AsyncClient, config: Config) -> None:
os.kill = MagicMock() os.kill = MagicMock()
config.set("Server", "local", True) config.set("Server", "local", True)
response = await controller_api.post('/shutdown') response = await client.post(app.url_path_for("shutdown"))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert os.kill.called assert os.kill.called
@pytest.mark.asyncio async def test_shutdown_non_local(app: FastAPI, client: AsyncClient, config: Config) -> None:
async def test_shutdown_non_local(controller_api, config):
config.set("Server", "local", False) config.set("Server", "local", False)
response = await controller_api.post('/shutdown') response = await client.post(app.url_path_for("shutdown"))
assert response.status_code == 403 assert response.status_code == status.HTTP_403_FORBIDDEN
# @pytest.mark.asyncio # @pytest.mark.asyncio
@ -60,8 +64,7 @@ async def test_shutdown_non_local(controller_api, config):
# assert response.status_code == 403 # assert response.status_code == 403
@pytest.mark.asyncio async def test_statistics_output(app: FastAPI, client: AsyncClient) -> None:
async def test_statistics_output(controller_api):
response = await controller_api.get('/statistics') response = await client.get(app.url_path_for("statistics"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK

View File

@ -16,11 +16,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.controller.drawing import Drawing from gns3server.controller.drawing import Drawing
from gns3server.controller.project import Project
pytestmark = pytest.mark.asyncio
@pytest.mark.asyncio async def test_create_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_create_drawing(controller_api, project):
params = { params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
@ -29,13 +35,12 @@ async def test_create_drawing(controller_api, project):
"z": 0 "z": 0
} }
response = await controller_api.post("/projects/{}/drawings".format(project.id), params) response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["drawing_id"] is not None assert response.json()["drawing_id"] is not None
@pytest.mark.asyncio async def test_get_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_get_drawing(controller_api, project):
params = { params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
@ -44,14 +49,17 @@ async def test_get_drawing(controller_api, project):
"z": 0 "z": 0
} }
response = await controller_api.post("/projects/{}/drawings".format(project.id), params) response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
response = await controller_api.get("/projects/{}/drawings/{}".format(project.id, response.json["drawing_id"])) response = await client.get(app.url_path_for(
"get_drawing",
project_id=project.id,
drawing_id=response.json()["drawing_id"])
)
assert response.status_code == 200 assert response.status_code == 200
assert response.json["x"] == 10 assert response.json()["x"] == 10
@pytest.mark.asyncio async def test_update_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_update_drawing(controller_api, project):
params = { params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
@ -60,14 +68,19 @@ async def test_update_drawing(controller_api, project):
"z": 0 "z": 0
} }
response = await controller_api.post("/projects/{}/drawings".format(project.id), params) response = await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
response = await controller_api.put("/projects/{}/drawings/{}".format(project.id, response.json["drawing_id"]), {"x": 42}) response = await client.put(app.url_path_for(
assert response.status_code == 200 "update_drawing",
assert response.json["x"] == 42 project_id=project.id,
drawing_id=response.json()["drawing_id"]),
json={"x": 42}
)
assert response.status_code == status.HTTP_200_OK
assert response.json()["x"] == 42
@pytest.mark.asyncio async def test_all_drawings(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_list_drawing(controller_api, project):
params = { params = {
"svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>', "svg": '<svg height="210" width="500"><line x1="0" y1="0" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" /></svg>',
@ -76,17 +89,20 @@ async def test_list_drawing(controller_api, project):
"z": 0 "z": 0
} }
await controller_api.post("/projects/{}/drawings".format(project.id), params) await client.post(app.url_path_for("create_drawing", project_id=project.id), json=params)
response = await controller_api.get("/projects/{}/drawings".format(project.id)) response = await client.get(app.url_path_for("get_drawings", project_id=project.id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert len(response.json) == 1 assert len(response.json()) == 1
@pytest.mark.asyncio async def test_delete_drawing(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_delete_drawing(controller_api, project):
drawing = Drawing(project) drawing = Drawing(project)
project._drawings = {drawing.id: drawing} project._drawings = {drawing.id: drawing}
response = await controller_api.delete("/projects/{}/drawings/{}".format(project.id, drawing.id)) response = await client.delete(app.url_path_for(
assert response.status_code == 204 "delete_drawing",
project_id=project.id,
drawing_id=drawing.id)
)
assert response.status_code == status.HTTP_204_NO_CONTENT
assert drawing.id not in project.drawings assert drawing.id not in project.drawings

View File

@ -16,39 +16,40 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
pytestmark = pytest.mark.asyncio
@pytest.mark.asyncio
async def test_list_vms(controller_api): async def test_list_vms(app: FastAPI, client: AsyncClient) -> None:
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]): with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.list", return_value=[{"vmname": "test"}]):
response = await controller_api.get('/gns3vm/engines/vmware/vms') response = await client.get(app.url_path_for("get_vms", engine="vmware"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == [ assert response.json() == [
{ {
"vmname": "test" "vmname": "test"
} }
] ]
@pytest.mark.asyncio async def test_engines(app: FastAPI, client: AsyncClient) -> None:
async def test_engines(controller_api):
response = await controller_api.get('/gns3vm/engines') response = await client.get(app.url_path_for("get_engines"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert len(response.json) > 0 assert len(response.json()) > 0
@pytest.mark.asyncio async def test_put_gns3vm(app: FastAPI, client: AsyncClient) -> None:
async def test_put_gns3vm(controller_api):
response = await controller_api.put('/gns3vm', {"vmname": "TEST VM"}) response = await client.put(app.url_path_for("update_gns3vm_settings"), json={"vmname": "TEST VM"})
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["vmname"] == "TEST VM" assert response.json()["vmname"] == "TEST VM"
@pytest.mark.asyncio async def test_get_gns3vm(app: FastAPI, client: AsyncClient) -> None:
async def test_get_gns3vm(controller_api): response = await client.get(app.url_path_for("get_gns3vm_settings"))
response = await controller_api.get('/gns3vm') assert response.status_code == status.HTTP_200_OK
assert response.status_code == 200

View File

@ -17,17 +17,25 @@
import pytest import pytest
from typing import Tuple
from fastapi import FastAPI, status
from httpx import AsyncClient
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from tests.utils import asyncio_patch, AsyncioMagicMock from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.controller.project import Project
from gns3server.controller.compute import Compute
from gns3server.controller.node import Node
from gns3server.controller.ports.ethernet_port import EthernetPort from gns3server.controller.ports.ethernet_port import EthernetPort
from gns3server.controller.link import Link, FILTERS from gns3server.controller.link import Link, FILTERS
from gns3server.controller.udp_link import UDPLink from gns3server.controller.udp_link import UDPLink
pytestmark = pytest.mark.asyncio
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def nodes(compute: Compute, project: Project) -> Tuple[Node, Node]:
async def nodes(compute, project):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
@ -40,8 +48,7 @@ async def nodes(compute, project):
return node1, node2 return node1, node2
@pytest.mark.asyncio async def test_create_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
async def test_create_link(controller_api, project, nodes):
node1, node2 = nodes node1, node2 = nodes
@ -51,7 +58,7 @@ async def test_create_link(controller_api, project, nodes):
} }
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = await controller_api.post("/projects/{}/links".format(project.id), { response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -73,16 +80,15 @@ async def test_create_link(controller_api, project, nodes):
}) })
assert mock.called assert mock.called
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["link_id"] is not None assert response.json()["link_id"] is not None
assert len(response.json["nodes"]) == 2 assert len(response.json()["nodes"]) == 2
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json()["nodes"][0]["label"]["x"] == 42
assert len(project.links) == 1 assert len(project.links) == 1
assert list(project.links.values())[0].filters == filters assert list(project.links.values())[0].filters == filters
@pytest.mark.asyncio async def test_create_link_failure(app: FastAPI, client: AsyncClient, compute: Compute, project: Project) -> None:
async def test_create_link_failure(controller_api, compute, project):
""" """
Make sure the link is deleted if we failed to create it. Make sure the link is deleted if we failed to create it.
@ -96,7 +102,7 @@ async def test_create_link_failure(controller_api, compute, project):
node1 = await project.add_node(compute, "node1", None, node_type="qemu") node1 = await project.add_node(compute, "node1", None, node_type="qemu")
node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)] node1._ports = [EthernetPort("E0", 0, 0, 3), EthernetPort("E0", 0, 0, 4)]
response = await controller_api.post("/projects/{}/links".format(project.id), { response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -116,16 +122,15 @@ async def test_create_link_failure(controller_api, compute, project):
] ]
}) })
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
assert len(project.links) == 0 assert len(project.links) == 0
@pytest.mark.asyncio async def test_get_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
async def test_get_link(controller_api, project, nodes):
node1, node2 = nodes node1, node2 = nodes
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = await controller_api.post("/projects/{}/links".format(project.id), { response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -146,19 +151,18 @@ async def test_get_link(controller_api, project, nodes):
}) })
assert mock.called assert mock.called
link_id = response.json["link_id"] link_id = response.json()["link_id"]
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json()["nodes"][0]["label"]["x"] == 42
response = await controller_api.get("/projects/{}/links/{}".format(project.id, link_id)) response = await client.get(app.url_path_for("get_link", project_id=project.id, link_id=link_id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json()["nodes"][0]["label"]["x"] == 42
@pytest.mark.asyncio async def test_update_link_suspend(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
async def test_update_link_suspend(controller_api, project, nodes):
node1, node2 = nodes node1, node2 = nodes
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = await controller_api.post("/projects/{}/links".format(project.id), { response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -179,10 +183,10 @@ async def test_update_link_suspend(controller_api, project, nodes):
}) })
assert mock.called assert mock.called
link_id = response.json["link_id"] link_id = response.json()["link_id"]
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json()["nodes"][0]["label"]["x"] == 42
response = await controller_api.put("/projects/{}/links/{}".format(project.id, link_id), { response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -203,14 +207,13 @@ async def test_update_link_suspend(controller_api, project, nodes):
"suspend": True "suspend": True
}) })
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["nodes"][0]["label"]["x"] == 64 assert response.json()["nodes"][0]["label"]["x"] == 64
assert response.json["suspend"] assert response.json()["suspend"]
assert response.json["filters"] == {} assert response.json()["filters"] == {}
@pytest.mark.asyncio async def test_update_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
async def test_update_link(controller_api, project, nodes):
filters = { filters = {
"latency": [10], "latency": [10],
@ -219,7 +222,7 @@ async def test_update_link(controller_api, project, nodes):
node1, node2 = nodes node1, node2 = nodes
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
response = await controller_api.post("/projects/{}/links".format(project.id), { response = await client.post(app.url_path_for("create_link", project_id=project.id), json={
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -240,10 +243,10 @@ async def test_update_link(controller_api, project, nodes):
}) })
assert mock.called assert mock.called
link_id = response.json["link_id"] link_id = response.json()["link_id"]
assert response.json["nodes"][0]["label"]["x"] == 42 assert response.json()["nodes"][0]["label"]["x"] == 42
response = await controller_api.put("/projects/{}/links/{}".format(project.id, link_id), { response = await client.put(app.url_path_for("update_link", project_id=project.id, link_id=link_id), json={
"nodes": [ "nodes": [
{ {
"node_id": node1.id, "node_id": node1.id,
@ -264,13 +267,12 @@ async def test_update_link(controller_api, project, nodes):
"filters": filters "filters": filters
}) })
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["nodes"][0]["label"]["x"] == 64 assert response.json()["nodes"][0]["label"]["x"] == 64
assert list(project.links.values())[0].filters == filters assert list(project.links.values())[0].filters == filters
@pytest.mark.asyncio async def test_list_link(app: FastAPI, client: AsyncClient, project: Project, nodes: Tuple[Node, Node]) -> None:
async def test_list_link(controller_api, project, nodes):
filters = { filters = {
"latency": [10], "latency": [10],
@ -291,51 +293,48 @@ async def test_list_link(controller_api, project, nodes):
} }
] ]
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as mock:
await controller_api.post("/projects/{}/links".format(project.id), { await client.post(app.url_path_for("create_link", project_id=project.id), json={
"nodes": nodes, "nodes": nodes,
"filters": filters "filters": filters
}) })
assert mock.called assert mock.called
response = await controller_api.get("/projects/{}/links".format(project.id)) response = await client.get(app.url_path_for("get_links", project_id=project.id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert len(response.json) == 1 assert len(response.json()) == 1
assert response.json[0]["filters"] == filters assert response.json()[0]["filters"] == filters
@pytest.mark.asyncio async def test_reset_link(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_reset_link(controller_api, project):
link = UDPLink(project) link = UDPLink(project)
project._links = {link.id: link} project._links = {link.id: link}
with asyncio_patch("gns3server.controller.udp_link.UDPLink.delete") as delete_mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.delete") as delete_mock:
with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as create_mock: with asyncio_patch("gns3server.controller.udp_link.UDPLink.create") as create_mock:
response = await controller_api.post("/projects/{}/links/{}/reset".format(project.id, link.id)) response = await client.post(app.url_path_for("reset_link", project_id=project.id, link_id=link.id))
assert delete_mock.called assert delete_mock.called
assert create_mock.called assert create_mock.called
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
@pytest.mark.asyncio async def test_start_capture(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_start_capture(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock: with asyncio_patch("gns3server.controller.link.Link.start_capture") as mock:
response = await controller_api.post("/projects/{}/links/{}/capture/start".format(project.id, link.id)) response = await client.post(app.url_path_for("start_capture", project_id=project.id, link_id=link.id), json={})
assert mock.called assert mock.called
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
@pytest.mark.asyncio async def test_stop_capture(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_stop_capture(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock: with asyncio_patch("gns3server.controller.link.Link.stop_capture") as mock:
response = await controller_api.post("/projects/{}/links/{}/capture/stop".format(project.id, link.id)) response = await client.post(app.url_path_for("stop_capture", project_id=project.id, link_id=link.id))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
# async def test_pcap(controller_api, http_client, project): # async def test_pcap(controller_api, http_client, project):
@ -359,24 +358,22 @@ async def test_stop_capture(controller_api, project):
# assert b'hello' == response.body # assert b'hello' == response.body
@pytest.mark.asyncio async def test_delete_link(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_delete_link(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with asyncio_patch("gns3server.controller.link.Link.delete") as mock: with asyncio_patch("gns3server.controller.link.Link.delete") as mock:
response = await controller_api.delete("/projects/{}/links/{}".format(project.id, link.id)) response = await client.delete(app.url_path_for("delete_link", project_id=project.id, link_id=link.id))
assert mock.called assert mock.called
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_list_filters(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_list_filters(controller_api, project):
link = Link(project) link = Link(project)
project._links = {link.id: link} project._links = {link.id: link}
with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock: with patch("gns3server.controller.link.Link.available_filters", return_value=FILTERS) as mock:
response = await controller_api.get("/projects/{}/links/{}/available_filters".format(project.id, link.id)) response = await client.get(app.url_path_for("get_filters", project_id=project.id, link_id=link.id))
assert mock.called assert mock.called
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == FILTERS assert response.json() == FILTERS

View File

@ -18,28 +18,34 @@
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from unittest.mock import MagicMock from unittest.mock import MagicMock
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock
from gns3server.controller.node import Node from gns3server.controller.node import Node
from gns3server.controller.project import Project
from gns3server.controller.compute import Compute
pytestmark = pytest.mark.asyncio
@pytest.fixture @pytest.fixture
def node(project, compute): def node(project: Project, compute: Compute) -> Node:
node = Node(project, compute, "test", node_type="vpcs") node = Node(project, compute, "test", node_type="vpcs")
project._nodes[node.id] = node project._nodes[node.id] = node
return node return node
@pytest.mark.asyncio async def test_create_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_create_node(controller_api, project, compute):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
response = await controller_api.post("/projects/{}/nodes".format(project.id), { response = await client.post(app.url_path_for("create_node", project_id=project.id), json={
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
@ -48,19 +54,18 @@ async def test_create_node(controller_api, project, compute):
} }
}) })
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "test" assert response.json()["name"] == "test"
assert "name" not in response.json["properties"] assert "name" not in response.json()["properties"]
@pytest.mark.asyncio async def test_list_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_list_node(controller_api, project, compute):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
await controller_api.post("/projects/{}/nodes".format(project.id), { await client.post(app.url_path_for("create_node", project_id=project.id), json={
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
@ -69,19 +74,18 @@ async def test_list_node(controller_api, project, compute):
} }
}) })
response = await controller_api.get("/projects/{}/nodes".format(project.id)) response = await client.get(app.url_path_for("get_nodes", project_id=project.id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json[0]["name"] == "test" assert response.json()[0]["name"] == "test"
@pytest.mark.asyncio async def test_get_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_get_node(controller_api, project, compute):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
response = await controller_api.post("/projects/{}/nodes".format(project.id), { response = await client.post(app.url_path_for("create_node", project_id=project.id), json={
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
@ -90,19 +94,18 @@ async def test_get_node(controller_api, project, compute):
} }
}) })
response = await controller_api.get("/projects/{}/nodes/{}".format(project.id, response.json["node_id"])) response = await client.get(app.url_path_for("get_node", project_id=project.id, node_id=response.json()["node_id"]))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "test" assert response.json()["name"] == "test"
@pytest.mark.asyncio async def test_update_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
async def test_update_node(controller_api, project, compute, node):
response = MagicMock() response = MagicMock()
response.json = {"console": 2048} response.json = {"console": 2048}
compute.put = AsyncioMagicMock(return_value=response) compute.put = AsyncioMagicMock(return_value=response)
response = await controller_api.put("/projects/{}/nodes/{}".format(project.id, node.id), { response = await client.put(app.url_path_for("update_node", project_id=project.id, node_id=node.id), json={
"name": "test", "name": "test",
"node_type": "vpcs", "node_type": "vpcs",
"compute_id": "example.com", "compute_id": "example.com",
@ -112,156 +115,167 @@ async def test_update_node(controller_api, project, compute, node):
}) })
assert response.status_code == 200 assert response.status_code == 200
assert response.json["name"] == "test" assert response.json()["name"] == "test"
assert "name" not in response.json["properties"] assert "name" not in response.json()["properties"]
@pytest.mark.asyncio async def test_start_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_start_all_nodes(controller_api, project, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/start".format(project.id)) response = await client.post(app.url_path_for("start_all_nodes", project_id=project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_stop_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_stop_all_nodes(controller_api, project, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/stop".format(project.id)) response = await client.post(app.url_path_for("stop_all_nodes", project_id=project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_suspend_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_suspend_all_nodes(controller_api, project, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/suspend".format(project.id)) response = await client.post(app.url_path_for("suspend_all_nodes", project_id=project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_reload_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_reload_all_nodes(controller_api, project, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/reload".format(project.id)) response = await client.post(app.url_path_for("reload_all_nodes", project_id=project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_reset_console_all_nodes(app: FastAPI, client: AsyncClient, project: Project, compute: Compute) -> None:
async def test_reset_console_all_nodes(controller_api, project, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/console/reset".format(project.id)) response = await client.post(app.url_path_for("reset_console_all_nodes", project_id=project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_start_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
async def test_start_node(controller_api, project, node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/start".format(project.id, node.id)) response = await client.post(app.url_path_for("start_node", project_id=project.id, node_id=node.id), json={})
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_stop_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
async def test_stop_node(controller_api, project, node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/stop".format(project.id, node.id)) response = await client.post(app.url_path_for("stop_node", project_id=project.id, node_id=node.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
async def test_suspend_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
@pytest.mark.asyncio
async def test_suspend_node(controller_api, project, node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/suspend".format(project.id, node.id)) response = await client.post(app.url_path_for("suspend_node", project_id=project.id, node_id=node.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_reload_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node):
async def test_reload_node(controller_api, project, node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/{}/reload".format(project.id, node.id)) response = await client.post(app.url_path_for("reload_node", project_id=project.id, node_id=node.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_duplicate_node(
async def test_duplicate_node(controller_api, project, compute, node): app: FastAPI,
client: AsyncClient,
project: Project,
compute: Compute,
node: Node) -> None:
response = MagicMock() response = MagicMock()
response.json({"console": 2035}) response.json({"console": 2035})
compute.post = AsyncioMagicMock(return_value=response) compute.post = AsyncioMagicMock(return_value=response)
response = await controller_api.post("/projects/{}/nodes/{}/duplicate".format(project.id, node.id), response = await client.post(app.url_path_for("duplicate_node", project_id=project.id, node_id=node.id),
{"x": 10, json={"x": 10, "y": 5, "z": 0})
"y": 5, assert response.status_code == status.HTTP_201_CREATED
"z": 0})
assert response.status_code == 201, response.body.decode()
@pytest.mark.asyncio async def test_delete_node(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
async def test_delete_node(controller_api, project, node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()
response = await controller_api.delete("/projects/{}/nodes/{}".format(project.id, node.id)) response = await client.delete(app.url_path_for("delete_node", project_id=project.id, node_id=node.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
@pytest.mark.asyncio async def test_dynamips_idle_pc(
async def test_dynamips_idle_pc(controller_api, project, compute, node): app: FastAPI,
client: AsyncClient,
project: Project,
compute: Compute,
node: Node) -> None:
response = MagicMock() response = MagicMock()
response.json = {"idlepc": "0x60606f54"} response.json = {"idlepc": "0x60606f54"}
compute.get = AsyncioMagicMock(return_value=response) compute.get = AsyncioMagicMock(return_value=response)
response = await controller_api.get("/projects/{}/nodes/{}/dynamips/auto_idlepc".format(project.id, node.id)) response = await client.get(app.url_path_for("auto_idlepc", project_id=project.id, node_id=node.id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["idlepc"] == "0x60606f54" assert response.json()["idlepc"] == "0x60606f54"
@pytest.mark.asyncio async def test_dynamips_idlepc_proposals(
async def test_dynamips_idlepc_proposals(controller_api, project, compute, node): app: FastAPI,
client: AsyncClient,
project: Project,
compute: Compute,
node: Node) -> None:
response = MagicMock() response = MagicMock()
response.json = ["0x60606f54", "0x33805a22"] response.json = ["0x60606f54", "0x33805a22"]
compute.get = AsyncioMagicMock(return_value=response) compute.get = AsyncioMagicMock(return_value=response)
response = await controller_api.get("/projects/{}/nodes/{}/dynamips/idlepc_proposals".format(project.id, node.id)) response = await client.get(app.url_path_for("idlepc_proposals", project_id=project.id, node_id=node.id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == ["0x60606f54", "0x33805a22"] assert response.json() == ["0x60606f54", "0x33805a22"]
@pytest.mark.asyncio async def test_get_file(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
async def test_get_file(controller_api, project, node, compute):
response = MagicMock() response = MagicMock()
response.body = b"world" response.body = b"world"
compute.http_query = AsyncioMagicMock(return_value=response) compute.http_query = AsyncioMagicMock(return_value=response)
response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id)) response = await client.get(app.url_path_for("get_file", project_id=project.id, node_id=node.id, file_path="hello"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.content == b'world' assert response.content == b'world'
compute.http_query.assert_called_with("GET", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), timeout=None, raw=True) compute.http_query.assert_called_with(
"GET",
"/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(
project_id=project.id,
node_id=node.id),
timeout=None,
raw=True)
response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id)) response = await client.get(app.url_path_for(
assert response.status_code == 404 "get_file",
project_id=project.id,
node_id=node.id,
file_path="../hello"))
assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_post_file(app: FastAPI, client: AsyncClient, project: Project, compute: Compute, node: Node) -> None:
async def test_post_file(controller_api, project, node, compute):
compute.http_query = AsyncioMagicMock() compute.http_query = AsyncioMagicMock()
response = await controller_api.post("/projects/{project_id}/nodes/{node_id}/files/hello".format(project_id=project.id, node_id=node.id), body=b"hello", raw=True) response = await client.post(app.url_path_for(
assert response.status_code == 201 "post_file",
project_id=project.id,
node_id=node.id,
file_path="hello"), content=b"hello")
assert response.status_code == status.HTTP_201_CREATED
compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True) compute.http_query.assert_called_with("POST", "/projects/{project_id}/files/project-files/vpcs/{node_id}/hello".format(project_id=project.id, node_id=node.id), data=b'hello', timeout=None, raw=True)
response = await controller_api.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id)) response = await client.get("/projects/{project_id}/nodes/{node_id}/files/../hello".format(project_id=project.id, node_id=node.id))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
# @pytest.mark.asyncio # @pytest.mark.asyncio

View File

@ -17,177 +17,173 @@
import uuid import uuid
import os import os
import pytest
import zipfile import zipfile
import json import json
import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from gns3server.controller import Controller
from gns3server.controller.project import Project
pytestmark = pytest.mark.asyncio
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
async def project(controller_api, controller):
u = str(uuid.uuid4()) u = str(uuid.uuid4())
params = {"name": "test", "project_id": u} params = {"name": "test", "project_id": u}
await controller_api.post("/projects", params) await client.post(app.url_path_for("create_project"), json=params)
return controller.get_project(u) return controller.get_project(u)
@pytest.mark.asyncio async def test_create_project_with_path(app: FastAPI, client: AsyncClient, controller: Controller, tmpdir) -> None:
async def test_create_project_with_path(controller_api, tmpdir):
response = await controller_api.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"}) params = {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"}
assert response.status_code == 201 response = await client.post(app.url_path_for("create_project"), json=params)
assert response.json["name"] == "test" assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json()["name"] == "test"
assert response.json["status"] == "opened" assert response.json()["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json()["status"] == "opened"
@pytest.mark.asyncio async def test_create_project_without_dir(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_create_project_without_dir(controller_api):
params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"} params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await controller_api.post("/projects", params) response = await client.post(app.url_path_for("create_project"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json()["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["name"] == "test" assert response.json()["name"] == "test"
@pytest.mark.asyncio async def test_create_project_with_uuid(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_create_project_with_uuid(controller_api):
params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"} params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await controller_api.post("/projects", params) response = await client.post(app.url_path_for("create_project"), json=params)
assert response.status_code == 201 assert response.status_code == 201
assert response.json["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json()["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["name"] == "test" assert response.json()["name"] == "test"
@pytest.mark.asyncio async def test_create_project_with_variables(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_create_project_with_variables(controller_api):
variables = [ variables = [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
] ]
params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables}
response = await controller_api.post("/projects", params) response = await client.post(app.url_path_for("create_project"), json=params)
assert response.status_code == 201 assert response.status_code == 201
assert response.json["variables"] == [ assert response.json()["variables"] == [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
] ]
@pytest.mark.asyncio async def test_create_project_with_supplier(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_create_project_with_supplier(controller_api):
supplier = { supplier = {
'logo': 'logo.png', 'logo': 'logo.png',
'url': 'http://example.com' 'url': 'http://example.com'
} }
params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "supplier": supplier} params = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "supplier": supplier}
response = await controller_api.post("/projects", params) response = await client.post(app.url_path_for("create_project"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["supplier"] == supplier assert response.json()["supplier"] == supplier
@pytest.mark.asyncio async def test_update_project(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_update_project(controller_api):
params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"} params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await controller_api.post("/projects", params) response = await client.post(app.url_path_for("create_project"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f" assert response.json()["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f"
assert response.json["name"] == "test" assert response.json()["name"] == "test"
params = {"name": "test2"} params = {"name": "test2"}
response = await controller_api.put("/projects/10010203-0405-0607-0809-0a0b0c0d0e0f", params) response = await client.put(app.url_path_for("update_project", project_id="10010203-0405-0607-0809-0a0b0c0d0e0f"),
assert response.status_code == 200 json=params)
assert response.json["name"] == "test2" assert response.status_code == status.HTTP_200_OK
assert response.json()["name"] == "test2"
@pytest.mark.asyncio async def test_update_project_with_variables(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_update_project_with_variables(controller_api):
variables = [ variables = [
{"name": "TEST1"}, {"name": "TEST1"},
{"name": "TEST2", "value": "value1"} {"name": "TEST2", "value": "value1"}
] ]
params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} params = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables}
response = await controller_api.post("/projects", params) response = await client.post(app.url_path_for("create_project"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
params = {"name": "test2"} params = {"name": "test2"}
response = await controller_api.put("/projects/10010203-0405-0607-0809-0a0b0c0d0e0f", params) response = await client.put(app.url_path_for("update_project", project_id="10010203-0405-0607-0809-0a0b0c0d0e0f"),
assert response.status_code == 200 json=params)
assert response.json["variables"] == variables assert response.status_code == status.HTTP_200_OK
assert response.json()["variables"] == variables
@pytest.mark.asyncio async def test_list_projects(app: FastAPI, client: AsyncClient, controller: Controller, tmpdir) -> None:
async def test_list_projects(controller_api, tmpdir):
await controller_api.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"}) params = {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"}
response = await controller_api.get("/projects") await client.post(app.url_path_for("create_project"), json=params)
assert response.status_code == 200 response = await client.get(app.url_path_for("get_projects"))
projects = response.json assert response.status_code == status.HTTP_200_OK
projects = response.json()
assert projects[0]["name"] == "test" assert projects[0]["name"] == "test"
@pytest.mark.asyncio async def test_get_project(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_get_project(controller_api, project):
response = await controller_api.get("/projects/{project_id}".format(project_id=project.id)) response = await client.get(app.url_path_for("get_project", project_id=project.id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "test" assert response.json()["name"] == "test"
@pytest.mark.asyncio async def test_delete_project(app: FastAPI, client: AsyncClient, project: Project, controller: Controller) -> None:
async def test_delete_project(controller_api, project, controller):
with asyncio_patch("gns3server.controller.project.Project.delete", return_value=True) as mock: with asyncio_patch("gns3server.controller.project.Project.delete", return_value=True) as mock:
response = await controller_api.delete("/projects/{project_id}".format(project_id=project.id)) response = await client.delete(app.url_path_for("delete_project", project_id=project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
assert project not in controller.projects assert project not in controller.projects
@pytest.mark.asyncio async def test_delete_project_invalid_uuid(app: FastAPI, client: AsyncClient) -> None:
async def test_delete_project_invalid_uuid(controller_api):
response = await controller_api.delete("/projects/{project_id}".format(project_id=uuid.uuid4())) response = await client.delete(app.url_path_for("delete_project", project_id=str(uuid.uuid4())))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_close_project(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_close_project(controller_api, project):
with asyncio_patch("gns3server.controller.project.Project.close", return_value=True) as mock: with asyncio_patch("gns3server.controller.project.Project.close", return_value=True) as mock:
response = await controller_api.post("/projects/{project_id}/close".format(project_id=project.id)) response = await client.post(app.url_path_for("close_project", project_id=project.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert mock.called assert mock.called
@pytest.mark.asyncio async def test_open_project(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_open_project(controller_api, project):
with asyncio_patch("gns3server.controller.project.Project.open", return_value=True) as mock: with asyncio_patch("gns3server.controller.project.Project.open", return_value=True) as mock:
response = await controller_api.post("/projects/{project_id}/open".format(project_id=project.id)) response = await client.post(app.url_path_for("open_project", project_id=project.id))
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert mock.called assert mock.called
@pytest.mark.asyncio async def test_load_project(app: FastAPI, client: AsyncClient, project: Project, config) -> None:
async def test_load_project(controller_api, project, config):
config.set("Server", "local", "true") config.set("Server", "local", "true")
with asyncio_patch("gns3server.controller.Controller.load_project", return_value=project) as mock: with asyncio_patch("gns3server.controller.Controller.load_project", return_value=project) as mock:
response = await controller_api.post("/projects/load", {"path": "/tmp/test.gns3"}) response = await client.post(app.url_path_for("load_project"), json={"path": "/tmp/test.gns3"})
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
mock.assert_called_with("/tmp/test.gns3") mock.assert_called_with("/tmp/test.gns3")
assert response.json["project_id"] == project.id assert response.json()["project_id"] == project.id
# @pytest.mark.asyncio # @pytest.mark.asyncio
@ -230,8 +226,7 @@ async def test_load_project(controller_api, project, config):
# assert project.status_code == "opened" # assert project.status_code == "opened"
@pytest.mark.asyncio async def test_export_with_images(app: FastAPI, client: AsyncClient, tmpdir, project: Project) -> None:
async def test_export_with_images(controller_api, tmpdir, project):
project.dump = MagicMock() project.dump = MagicMock()
os.makedirs(project.path, exist_ok=True) os.makedirs(project.path, exist_ok=True)
@ -258,8 +253,9 @@ async def test_export_with_images(controller_api, tmpdir, project):
json.dump(topology, f) json.dump(topology, f)
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS")): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS")):
response = await controller_api.get("/projects/{project_id}/export?include_images=yes".format(project_id=project.id)) response = await client.get(app.url_path_for("export_project", project_id=project.id),
assert response.status_code == 200 params={"include_images": "yes"})
assert response.status_code == status.HTTP_200_OK
assert response.headers['CONTENT-TYPE'] == 'application/gns3project' assert response.headers['CONTENT-TYPE'] == 'application/gns3project'
assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name)
@ -273,8 +269,7 @@ async def test_export_with_images(controller_api, tmpdir, project):
myzip.getinfo("images/IOS/test.image") myzip.getinfo("images/IOS/test.image")
@pytest.mark.asyncio async def test_export_without_images(app: FastAPI, client: AsyncClient, tmpdir, project: Project) -> None:
async def test_export_without_images(controller_api, tmpdir, project):
project.dump = MagicMock() project.dump = MagicMock()
os.makedirs(project.path, exist_ok=True) os.makedirs(project.path, exist_ok=True)
@ -301,8 +296,9 @@ async def test_export_without_images(controller_api, tmpdir, project):
json.dump(topology, f) json.dump(topology, f)
with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),): with patch("gns3server.compute.Dynamips.get_images_directory", return_value=str(tmpdir / "IOS"),):
response = await controller_api.get("/projects/{project_id}/export?include_images=0".format(project_id=project.id)) response = await client.get(app.url_path_for("export_project", project_id=project.id),
assert response.status_code == 200 params={"include_images": "0"})
assert response.status_code == status.HTTP_200_OK
assert response.headers['CONTENT-TYPE'] == 'application/gns3project' assert response.headers['CONTENT-TYPE'] == 'application/gns3project'
assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name) assert response.headers['CONTENT-DISPOSITION'] == 'attachment; filename="{}.gns3project"'.format(project.name)
@ -318,50 +314,51 @@ async def test_export_without_images(controller_api, tmpdir, project):
myzip.getinfo("images/IOS/test.image") myzip.getinfo("images/IOS/test.image")
@pytest.mark.asyncio async def test_get_file(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_get_file(controller_api, project):
os.makedirs(project.path, exist_ok=True) os.makedirs(project.path, exist_ok=True)
with open(os.path.join(project.path, 'hello'), 'w+') as f: with open(os.path.join(project.path, 'hello'), 'w+') as f:
f.write('world') f.write('world')
response = await controller_api.get("/projects/{project_id}/files/hello".format(project_id=project.id)) response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="hello"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.content == b"world" assert response.content == b"world"
response = await controller_api.get("/projects/{project_id}/files/false".format(project_id=project.id)) response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="false"))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
response = await controller_api.get("/projects/{project_id}/files/../hello".format(project_id=project.id)) response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="../hello"))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_write_file(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_write_file(controller_api, project):
response = await controller_api.post("/projects/{project_id}/files/hello".format(project_id=project.id), body="world", raw=True) response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="hello"),
assert response.status_code == 204 content=b"world")
assert response.status_code == status.HTTP_204_NO_CONTENT
with open(os.path.join(project.path, "hello")) as f: with open(os.path.join(project.path, "hello")) as f:
assert f.read() == "world" assert f.read() == "world"
response = await controller_api.post("/projects/{project_id}/files/../hello".format(project_id=project.id), raw=True) response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="../hello"))
assert response.status_code == 404 assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.asyncio async def test_write_and_get_file_with_leading_slashes_in_filename(
async def test_write_and_get_file_with_leading_slashes_in_filename(controller_api, project): app: FastAPI,
client: AsyncClient,
project: Project) -> None:
response = await controller_api.post("/projects/{project_id}/files//hello".format(project_id=project.id), body="world", raw=True) response = await client.post(app.url_path_for("write_file", project_id=project.id, file_path="//hello"),
assert response.status_code == 204 content=b"world")
assert response.status_code == status.HTTP_204_NO_CONTENT
response = await controller_api.get("/projects/{project_id}/files//hello".format(project_id=project.id), raw=True) response = await client.get(app.url_path_for("get_file", project_id=project.id, file_path="//hello"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.content == b"world" assert response.content == b"world"
@pytest.mark.asyncio async def test_import(app: FastAPI, client: AsyncClient, tmpdir, controller: Controller) -> None:
async def test_import(controller_api, tmpdir, controller):
with zipfile.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip: with zipfile.ZipFile(str(tmpdir / "test.zip"), 'w') as myzip:
myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}') myzip.writestr("project.gns3", b'{"project_id": "c6992992-ac72-47dc-833b-54aa334bcd05", "version": "2.0.0", "name": "test"}')
@ -369,8 +366,8 @@ async def test_import(controller_api, tmpdir, controller):
project_id = str(uuid.uuid4()) project_id = str(uuid.uuid4())
with open(str(tmpdir / "test.zip"), "rb") as f: with open(str(tmpdir / "test.zip"), "rb") as f:
response = await controller_api.post("/projects/{project_id}/import".format(project_id=project_id), body=f.read(), raw=True) response = await client.post(app.url_path_for("import_project", project_id=project_id), content=f.read())
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
project = controller.get_project(project_id) project = controller.get_project(project_id)
with open(os.path.join(project.path, "demo")) as f: with open(os.path.join(project.path, "demo")) as f:
@ -378,9 +375,8 @@ async def test_import(controller_api, tmpdir, controller):
assert content == "hello" assert content == "hello"
@pytest.mark.asyncio async def test_duplicate(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_duplicate(controller_api, project):
response = await controller_api.post("/projects/{project_id}/duplicate".format(project_id=project.id), {"name": "hello"}) response = await client.post(app.url_path_for("duplicate_project", project_id=project.id), json={"name": "hello"})
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == "hello" assert response.json()["name"] == "hello"

View File

@ -19,54 +19,57 @@ import os
import uuid import uuid
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.controller import Controller
from gns3server.controller.project import Project
from gns3server.controller.snapshot import Snapshot
pytestmark = pytest.mark.asyncio
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def project(app: FastAPI, client: AsyncClient, controller: Controller) -> Project:
async def project(controller_api, controller):
u = str(uuid.uuid4()) u = str(uuid.uuid4())
params = {"name": "test", "project_id": u} params = {"name": "test", "project_id": u}
await controller_api.post("/projects", params) await client.post(app.url_path_for("create_project"), json=params)
project = controller.get_project(u) project = controller.get_project(u)
return project return project
@pytest.fixture @pytest.fixture
@pytest.mark.asyncio async def snapshot(project: Project):
async def snapshot(project):
snapshot = await project.snapshot("test") snapshot = await project.snapshot("test")
return snapshot return snapshot
@pytest.mark.asyncio async def test_list_snapshots(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
async def test_list_snapshots(controller_api, project, snapshot):
assert snapshot.name == "test" assert snapshot.name == "test"
response = await controller_api.get("/projects/{}/snapshots".format(project.id)) response = await client.get(app.url_path_for("get_snapshots", project_id=project.id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert len(response.json) == 1 assert len(response.json()) == 1
@pytest.mark.asyncio async def test_delete_snapshot(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
async def test_delete_snapshot(controller_api, project, snapshot):
response = await controller_api.delete("/projects/{}/snapshots/{}".format(project.id, snapshot.id)) response = await client.delete(app.url_path_for("delete_snapshot", project_id=project.id, snapshot_id=snapshot.id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
assert not os.path.exists(snapshot.path) assert not os.path.exists(snapshot.path)
@pytest.mark.asyncio async def test_restore_snapshot(app: FastAPI, client: AsyncClient, project: Project, snapshot: Snapshot) -> None:
async def test_restore_snapshot(controller_api, project, snapshot):
response = await controller_api.post("/projects/{}/snapshots/{}/restore".format(project.id, snapshot.id)) response = await client.post(app.url_path_for("restore_snapshot", project_id=project.id, snapshot_id=snapshot.id))
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["name"] == project.name assert response.json()["name"] == project.name
@pytest.mark.asyncio async def test_create_snapshot(app: FastAPI, client: AsyncClient, project: Project) -> None:
async def test_create_snapshot(controller_api, project):
response = await controller_api.post("/projects/{}/snapshots".format(project.id), {"name": "snap1"}) response = await client.post(app.url_path_for("create_snapshot", project_id=project.id), json={"name": "snap1"})
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1 assert len(os.listdir(os.path.join(project.path, "snapshots"))) == 1

View File

@ -20,44 +20,49 @@ import pytest
import os import os
import urllib.parse import urllib.parse
from fastapi import FastAPI, status
from httpx import AsyncClient
@pytest.mark.asyncio from gns3server.controller import Controller
async def test_symbols(controller_api):
response = await controller_api.get('/symbols') pytestmark = pytest.mark.asyncio
assert response.status_code == 200
async def test_symbols(app: FastAPI, client: AsyncClient) -> None:
response = await client.get(app.url_path_for("get_symbols"))
assert response.status_code == status.HTTP_200_OK
assert { assert {
'symbol_id': ':/symbols/classic/firewall.svg', 'symbol_id': ':/symbols/classic/firewall.svg',
'filename': 'firewall.svg', 'filename': 'firewall.svg',
'builtin': True, 'builtin': True,
'theme': 'Classic' 'theme': 'Classic'
} in response.json } in response.json()
@pytest.mark.asyncio async def test_get(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_get(controller_api, controller):
controller.symbols.theme = "Classic" controller.symbols.theme = "Classic"
response = await controller_api.get('/symbols/' + urllib.parse.quote(':/symbols/classic/firewall.svg') + '/raw') url = app.url_path_for("get_symbol", symbol_id=urllib.parse.quote(':/symbols/classic/firewall.svg'))
assert response.status_code == 200 response = await client.get(url)
assert response.status_code == status.HTTP_200_OK
assert response.headers['CONTENT-TYPE'] == 'image/svg+xml' assert response.headers['CONTENT-TYPE'] == 'image/svg+xml'
assert response.headers['CONTENT-LENGTH'] == '9381' assert response.headers['CONTENT-LENGTH'] == '9381'
assert '</svg>' in response.text assert '</svg>' in response.text
# Reply with the default symbol # Reply with the default symbol
response = await controller_api.get('/symbols/404.png/raw') response = await client.get(app.url_path_for("get_symbol", symbol_id="404.png"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
@pytest.mark.asyncio async def test_upload(app: FastAPI, client: AsyncClient, symbols_dir: str) -> None:
async def test_upload(controller_api, symbols_dir):
response = await controller_api.post("/symbols/test2/raw", body=b"TEST", raw=True) response = await client.post(app.url_path_for("upload_symbol", symbol_id="test2"), content=b"TEST")
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
with open(os.path.join(symbols_dir, "test2")) as f: with open(os.path.join(symbols_dir, "test2")) as f:
assert f.read() == "TEST" assert f.read() == "TEST"
response = await controller_api.get('/symbols/test2/raw') response = await client.get(app.url_path_for("get_symbol", symbol_id="test2"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK

View File

@ -19,13 +19,16 @@ import pytest
import uuid import uuid
from pathlib import Path from pathlib import Path
from tests.utils import asyncio_patch from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.controller import Controller
from gns3server.controller.template import Template from gns3server.controller.template import Template
pytestmark = pytest.mark.asyncio
@pytest.mark.asyncio
async def test_template_list(controller_api, controller): async def test_template_list(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
id = str(uuid.uuid4()) id = str(uuid.uuid4())
controller.template_manager.load_templates() controller.template_manager.load_templates()
@ -37,13 +40,12 @@ async def test_template_list(controller_api, controller):
"default_name_format": "{name}-{0}", "default_name_format": "{name}-{0}",
"compute_id": "local" "compute_id": "local"
}) })
response = await controller_api.get("/templates") response = await client.get(app.url_path_for("get_templates"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert len(response.json) > 0 assert len(response.json()) > 0
@pytest.mark.asyncio async def test_template_create_without_id(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_template_create_without_id(controller_api, controller):
params = {"base_script_file": "vpcs_base_config.txt", params = {"base_script_file": "vpcs_base_config.txt",
"category": "guest", "category": "guest",
@ -55,14 +57,13 @@ async def test_template_create_without_id(controller_api, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
assert len(controller.template_manager.templates) == 1 assert len(controller.template_manager.templates) == 1
@pytest.mark.asyncio async def test_template_create_with_id(app: FastAPI, client: AsyncClient, controller: Controller):
async def test_template_create_with_id(controller_api, controller):
params = {"template_id": str(uuid.uuid4()), params = {"template_id": str(uuid.uuid4()),
"base_script_file": "vpcs_base_config.txt", "base_script_file": "vpcs_base_config.txt",
@ -75,14 +76,13 @@ async def test_template_create_with_id(controller_api, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
assert len(controller.template_manager.templates) == 1 assert len(controller.template_manager.templates) == 1
@pytest.mark.asyncio async def test_template_create_wrong_type(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_template_create_wrong_type(controller_api, controller):
params = {"template_id": str(uuid.uuid4()), params = {"template_id": str(uuid.uuid4()),
"base_script_file": "vpcs_base_config.txt", "base_script_file": "vpcs_base_config.txt",
@ -95,13 +95,12 @@ async def test_template_create_wrong_type(controller_api, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "invalid_template_type"} "template_type": "invalid_template_type"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 422 assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
assert len(controller.template_manager.templates) == 0 assert len(controller.template_manager.templates) == 0
@pytest.mark.asyncio async def test_template_get(app: FastAPI, client: AsyncClient) -> None:
async def test_template_get(controller_api):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -115,16 +114,15 @@ async def test_template_get(controller_api):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await controller_api.get("/templates/{}".format(template_id)) response = await client.get(app.url_path_for("get_template", template_id=template_id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["template_id"] == template_id assert response.json()["template_id"] == template_id
@pytest.mark.asyncio async def test_template_update(app: FastAPI, client: AsyncClient) -> None:
async def test_template_update(controller_api):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -138,22 +136,21 @@ async def test_template_update(controller_api):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await controller_api.get("/templates/{}".format(template_id)) response = await client.get(app.url_path_for("get_template", template_id=template_id))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["template_id"] == template_id assert response.json()["template_id"] == template_id
params["name"] = "VPCS_TEST_RENAMED" params["name"] = "VPCS_TEST_RENAMED"
response = await controller_api.put("/templates/{}".format(template_id), params) response = await client.put(app.url_path_for("update_template", template_id=template_id), json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json["name"] == "VPCS_TEST_RENAMED" assert response.json()["name"] == "VPCS_TEST_RENAMED"
@pytest.mark.asyncio async def test_template_delete(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_template_delete(controller_api, controller):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -167,23 +164,22 @@ async def test_template_delete(controller_api, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await controller_api.get("/templates") response = await client.get(app.url_path_for("get_templates"))
assert len(response.json) == 1 assert len(response.json()) == 1
assert len(controller.template_manager._templates) == 1 assert len(controller.template_manager._templates) == 1
response = await controller_api.delete("/templates/{}".format(template_id)) response = await client.delete(app.url_path_for("delete_template", template_id=template_id))
assert response.status_code == 204 assert response.status_code == status.HTTP_204_NO_CONTENT
response = await controller_api.get("/templates") response = await client.get(app.url_path_for("get_templates"))
assert len(response.json) == 0 assert len(response.json()) == 0
assert len(controller.template_manager.templates) == 0 assert len(controller.template_manager.templates) == 0
@pytest.mark.asyncio async def test_template_duplicate(app: FastAPI, client: AsyncClient, controller: Controller) -> None:
async def test_template_duplicate(controller_api, controller):
template_id = str(uuid.uuid4()) template_id = str(uuid.uuid4())
params = {"template_id": template_id, params = {"template_id": template_id,
@ -197,23 +193,22 @@ async def test_template_duplicate(controller_api, controller):
"symbol": ":/symbols/vpcs_guest.svg", "symbol": ":/symbols/vpcs_guest.svg",
"template_type": "vpcs"} "template_type": "vpcs"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
response = await controller_api.post("/templates/{}/duplicate".format(template_id)) response = await client.post(app.url_path_for("duplicate_template", template_id=template_id))
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] != template_id assert response.json()["template_id"] != template_id
params.pop("template_id") params.pop("template_id")
for param, value in params.items(): for param, value in params.items():
assert response.json[param] == value assert response.json()[param] == value
response = await controller_api.get("/templates") response = await client.get(app.url_path_for("get_templates"))
assert len(response.json) == 2 assert len(response.json()) == 2
assert len(controller.template_manager.templates) == 2 assert len(controller.template_manager.templates) == 2
@pytest.mark.asyncio async def test_c7200_dynamips_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_c7200_dynamips_template_create(controller_api):
params = {"name": "Cisco c7200 template", params = {"name": "Cisco c7200 template",
"platform": "c7200", "platform": "c7200",
@ -221,9 +216,9 @@ async def test_c7200_dynamips_template_create(controller_api):
"image": "c7200-adventerprisek9-mz.124-24.T5.image", "image": "c7200-adventerprisek9-mz.124-24.T5.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "dynamips", expected_response = {"template_type": "dynamips",
"auto_delete_disks": False, "auto_delete_disks": False,
@ -255,11 +250,10 @@ async def test_c7200_dynamips_template_create(controller_api):
"system_id": "FTX0945W0MY"} "system_id": "FTX0945W0MY"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_c3745_dynamips_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_c3745_dynamips_template_create(controller_api):
params = {"name": "Cisco c3745 template", params = {"name": "Cisco c3745 template",
"platform": "c3745", "platform": "c3745",
@ -267,9 +261,9 @@ async def test_c3745_dynamips_template_create(controller_api):
"image": "c3745-adventerprisek9-mz.124-25d.image", "image": "c3745-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "dynamips", expected_response = {"template_type": "dynamips",
"auto_delete_disks": False, "auto_delete_disks": False,
@ -300,11 +294,10 @@ async def test_c3745_dynamips_template_create(controller_api):
"system_id": "FTX0945W0MY"} "system_id": "FTX0945W0MY"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_c3725_dynamips_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_c3725_dynamips_template_create(controller_api):
params = {"name": "Cisco c3725 template", params = {"name": "Cisco c3725 template",
"platform": "c3725", "platform": "c3725",
@ -312,9 +305,9 @@ async def test_c3725_dynamips_template_create(controller_api):
"image": "c3725-adventerprisek9-mz.124-25d.image", "image": "c3725-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "dynamips", expected_response = {"template_type": "dynamips",
"auto_delete_disks": False, "auto_delete_disks": False,
@ -345,11 +338,10 @@ async def test_c3725_dynamips_template_create(controller_api):
"system_id": "FTX0945W0MY"} "system_id": "FTX0945W0MY"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_c3600_dynamips_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_c3600_dynamips_template_create(controller_api):
params = {"name": "Cisco c3600 template", params = {"name": "Cisco c3600 template",
"platform": "c3600", "platform": "c3600",
@ -358,9 +350,9 @@ async def test_c3600_dynamips_template_create(controller_api):
"image": "c3660-a3jk9s-mz.124-25d.image", "image": "c3660-a3jk9s-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "dynamips", expected_response = {"template_type": "dynamips",
"auto_delete_disks": False, "auto_delete_disks": False,
@ -392,11 +384,10 @@ async def test_c3600_dynamips_template_create(controller_api):
"system_id": "FTX0945W0MY"} "system_id": "FTX0945W0MY"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_c3600_dynamips_template_create_wrong_chassis(app: FastAPI, client: AsyncClient) -> None:
async def test_c3600_dynamips_template_create_wrong_chassis(controller_api):
params = {"name": "Cisco c3600 template", params = {"name": "Cisco c3600 template",
"platform": "c3600", "platform": "c3600",
@ -405,12 +396,11 @@ async def test_c3600_dynamips_template_create_wrong_chassis(controller_api):
"image": "c3660-a3jk9s-mz.124-25d.image", "image": "c3660-a3jk9s-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio async def test_c2691_dynamips_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_c2691_dynamips_template_create(controller_api):
params = {"name": "Cisco c2691 template", params = {"name": "Cisco c2691 template",
"platform": "c2691", "platform": "c2691",
@ -418,9 +408,9 @@ async def test_c2691_dynamips_template_create(controller_api):
"image": "c2691-adventerprisek9-mz.124-25d.image", "image": "c2691-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "dynamips", expected_response = {"template_type": "dynamips",
"auto_delete_disks": False, "auto_delete_disks": False,
@ -451,11 +441,10 @@ async def test_c2691_dynamips_template_create(controller_api):
"system_id": "FTX0945W0MY"} "system_id": "FTX0945W0MY"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_c2600_dynamips_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_c2600_dynamips_template_create(controller_api):
params = {"name": "Cisco c2600 template", params = {"name": "Cisco c2600 template",
"platform": "c2600", "platform": "c2600",
@ -464,9 +453,9 @@ async def test_c2600_dynamips_template_create(controller_api):
"image": "c2600-adventerprisek9-mz.124-25d.image", "image": "c2600-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "dynamips", expected_response = {"template_type": "dynamips",
"auto_delete_disks": False, "auto_delete_disks": False,
@ -498,11 +487,10 @@ async def test_c2600_dynamips_template_create(controller_api):
"system_id": "FTX0945W0MY"} "system_id": "FTX0945W0MY"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_c2600_dynamips_template_create_wrong_chassis(app: FastAPI, client: AsyncClient) -> None:
async def test_c2600_dynamips_template_create_wrong_chassis(controller_api):
params = {"name": "Cisco c2600 template", params = {"name": "Cisco c2600 template",
"platform": "c2600", "platform": "c2600",
@ -511,12 +499,11 @@ async def test_c2600_dynamips_template_create_wrong_chassis(controller_api):
"image": "c2600-adventerprisek9-mz.124-25d.image", "image": "c2600-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio async def test_c1700_dynamips_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_c1700_dynamips_template_create(controller_api):
params = {"name": "Cisco c1700 template", params = {"name": "Cisco c1700 template",
"platform": "c1700", "platform": "c1700",
@ -525,9 +512,9 @@ async def test_c1700_dynamips_template_create(controller_api):
"image": "c1700-adventerprisek9-mz.124-25d.image", "image": "c1700-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "dynamips", expected_response = {"template_type": "dynamips",
"auto_delete_disks": False, "auto_delete_disks": False,
@ -559,11 +546,10 @@ async def test_c1700_dynamips_template_create(controller_api):
"system_id": "FTX0945W0MY"} "system_id": "FTX0945W0MY"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_c1700_dynamips_template_create_wrong_chassis(app: FastAPI, client: AsyncClient) -> None:
async def test_c1700_dynamips_template_create_wrong_chassis(controller_api):
params = {"name": "Cisco c1700 template", params = {"name": "Cisco c1700 template",
"platform": "c1700", "platform": "c1700",
@ -572,12 +558,11 @@ async def test_c1700_dynamips_template_create_wrong_chassis(controller_api):
"image": "c1700-adventerprisek9-mz.124-25d.image", "image": "c1700-adventerprisek9-mz.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio async def test_dynamips_template_create_wrong_platform(app: FastAPI, client: AsyncClient) -> None:
async def test_dynamips_template_create_wrong_platform(controller_api):
params = {"name": "Cisco c3900 template", params = {"name": "Cisco c3900 template",
"platform": "c3900", "platform": "c3900",
@ -585,12 +570,11 @@ async def test_dynamips_template_create_wrong_platform(controller_api):
"image": "c3900-test.124-25d.image", "image": "c3900-test.124-25d.image",
"template_type": "dynamips"} "template_type": "dynamips"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio async def test_iou_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_iou_template_create(controller_api):
image_path = str(Path("/path/to/i86bi_linux-ipbase-ms-12.4.bin")) image_path = str(Path("/path/to/i86bi_linux-ipbase-ms-12.4.bin"))
params = {"name": "IOU template", params = {"name": "IOU template",
@ -598,9 +582,9 @@ async def test_iou_template_create(controller_api):
"path": image_path, "path": image_path,
"template_type": "iou"} "template_type": "iou"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "iou", expected_response = {"template_type": "iou",
"builtin": False, "builtin": False,
@ -622,20 +606,19 @@ async def test_iou_template_create(controller_api):
"l1_keepalives": False} "l1_keepalives": False}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_docker_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_docker_template_create(controller_api):
params = {"name": "Docker template", params = {"name": "Docker template",
"compute_id": "local", "compute_id": "local",
"image": "gns3/endhost:latest", "image": "gns3/endhost:latest",
"template_type": "docker"} "template_type": "docker"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"adapters": 1, expected_response = {"adapters": 1,
"template_type": "docker", "template_type": "docker",
@ -657,11 +640,10 @@ async def test_docker_template_create(controller_api):
"custom_adapters": []} "custom_adapters": []}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_qemu_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_qemu_template_create(controller_api):
params = {"name": "Qemu template", params = {"name": "Qemu template",
"compute_id": "local", "compute_id": "local",
@ -670,9 +652,9 @@ async def test_qemu_template_create(controller_api):
"ram": 512, "ram": 512,
"template_type": "qemu"} "template_type": "qemu"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"adapter_type": "e1000", expected_response = {"adapter_type": "e1000",
"adapters": 1, "adapters": 1,
@ -717,11 +699,10 @@ async def test_qemu_template_create(controller_api):
"custom_adapters": []} "custom_adapters": []}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_vmware_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_vmware_template_create(controller_api):
vmx_path = str(Path("/path/to/vm.vmx")) vmx_path = str(Path("/path/to/vm.vmx"))
params = {"name": "VMware template", params = {"name": "VMware template",
@ -729,9 +710,9 @@ async def test_vmware_template_create(controller_api):
"template_type": "vmware", "template_type": "vmware",
"vmx_path": vmx_path} "vmx_path": vmx_path}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"adapter_type": "e1000", expected_response = {"adapter_type": "e1000",
"adapters": 1, "adapters": 1,
@ -755,20 +736,19 @@ async def test_vmware_template_create(controller_api):
"custom_adapters": []} "custom_adapters": []}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_virtualbox_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_virtualbox_template_create(controller_api):
params = {"name": "VirtualBox template", params = {"name": "VirtualBox template",
"compute_id": "local", "compute_id": "local",
"template_type": "virtualbox", "template_type": "virtualbox",
"vmname": "My VirtualBox VM"} "vmname": "My VirtualBox VM"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"adapter_type": "Intel PRO/1000 MT Desktop (82540EM)", expected_response = {"adapter_type": "Intel PRO/1000 MT Desktop (82540EM)",
"adapters": 1, "adapters": 1,
@ -793,19 +773,18 @@ async def test_virtualbox_template_create(controller_api):
"custom_adapters": []} "custom_adapters": []}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_vpcs_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_vpcs_template_create(controller_api):
params = {"name": "VPCS template", params = {"name": "VPCS template",
"compute_id": "local", "compute_id": "local",
"template_type": "vpcs"} "template_type": "vpcs"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "vpcs", expected_response = {"template_type": "vpcs",
"base_script_file": "vpcs_base_config.txt", "base_script_file": "vpcs_base_config.txt",
@ -819,19 +798,18 @@ async def test_vpcs_template_create(controller_api):
"symbol": ":/symbols/vpcs_guest.svg"} "symbol": ":/symbols/vpcs_guest.svg"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_ethernet_switch_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_ethernet_switch_template_create(controller_api):
params = {"name": "Ethernet switch template", params = {"name": "Ethernet switch template",
"compute_id": "local", "compute_id": "local",
"template_type": "ethernet_switch"} "template_type": "ethernet_switch"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "ethernet_switch", expected_response = {"template_type": "ethernet_switch",
"builtin": False, "builtin": False,
@ -840,49 +818,49 @@ async def test_ethernet_switch_template_create(controller_api):
"console_type": "none", "console_type": "none",
"default_name_format": "Switch{0}", "default_name_format": "Switch{0}",
"name": "Ethernet switch template", "name": "Ethernet switch template",
"ports_mapping": [{"ethertype": "", "ports_mapping": [{"ethertype": "0x8100",
"name": "Ethernet0", "name": "Ethernet0",
"port_number": 0, "port_number": 0,
"type": "access", "type": "access",
"vlan": 1 "vlan": 1
}, },
{"ethertype": "", {"ethertype": "0x8100",
"name": "Ethernet1", "name": "Ethernet1",
"port_number": 1, "port_number": 1,
"type": "access", "type": "access",
"vlan": 1 "vlan": 1
}, },
{"ethertype": "", {"ethertype": "0x8100",
"name": "Ethernet2", "name": "Ethernet2",
"port_number": 2, "port_number": 2,
"type": "access", "type": "access",
"vlan": 1 "vlan": 1
}, },
{"ethertype": "", {"ethertype": "0x8100",
"name": "Ethernet3", "name": "Ethernet3",
"port_number": 3, "port_number": 3,
"type": "access", "type": "access",
"vlan": 1 "vlan": 1
}, },
{"ethertype": "", {"ethertype": "0x8100",
"name": "Ethernet4", "name": "Ethernet4",
"port_number": 4, "port_number": 4,
"type": "access", "type": "access",
"vlan": 1 "vlan": 1
}, },
{"ethertype": "", {"ethertype": "0x8100",
"name": "Ethernet5", "name": "Ethernet5",
"port_number": 5, "port_number": 5,
"type": "access", "type": "access",
"vlan": 1 "vlan": 1
}, },
{"ethertype": "", {"ethertype": "0x8100",
"name": "Ethernet6", "name": "Ethernet6",
"port_number": 6, "port_number": 6,
"type": "access", "type": "access",
"vlan": 1 "vlan": 1
}, },
{"ethertype": "", {"ethertype": "0x8100",
"name": "Ethernet7", "name": "Ethernet7",
"port_number": 7, "port_number": 7,
"type": "access", "type": "access",
@ -891,19 +869,18 @@ async def test_ethernet_switch_template_create(controller_api):
"symbol": ":/symbols/ethernet_switch.svg"} "symbol": ":/symbols/ethernet_switch.svg"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_cloud_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_cloud_template_create(controller_api):
params = {"name": "Cloud template", params = {"name": "Cloud template",
"compute_id": "local", "compute_id": "local",
"template_type": "cloud"} "template_type": "cloud"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"template_type": "cloud", expected_response = {"template_type": "cloud",
"builtin": False, "builtin": False,
@ -919,19 +896,18 @@ async def test_cloud_template_create(controller_api):
"remote_console_http_path": "/"} "remote_console_http_path": "/"}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
@pytest.mark.asyncio async def test_ethernet_hub_template_create(app: FastAPI, client: AsyncClient) -> None:
async def test_ethernet_hub_template_create(controller_api):
params = {"name": "Ethernet hub template", params = {"name": "Ethernet hub template",
"compute_id": "local", "compute_id": "local",
"template_type": "ethernet_hub"} "template_type": "ethernet_hub"}
response = await controller_api.post("/templates", params) response = await client.post(app.url_path_for("create_template"), json=params)
assert response.status_code == 201 assert response.status_code == status.HTTP_201_CREATED
assert response.json["template_id"] is not None assert response.json()["template_id"] is not None
expected_response = {"ports_mapping": [{"port_number": 0, expected_response = {"ports_mapping": [{"port_number": 0,
"name": "Ethernet0" "name": "Ethernet0"
@ -966,7 +942,7 @@ async def test_ethernet_hub_template_create(controller_api):
"builtin": False} "builtin": False}
for item, value in expected_response.items(): for item, value in expected_response.items():
assert response.json.get(item) == value assert response.json().get(item) == value
# @pytest.mark.asyncio # @pytest.mark.asyncio
@ -982,9 +958,9 @@ async def test_ethernet_hub_template_create(controller_api):
# "compute_id": "example.com" # "compute_id": "example.com"
# })} # })}
# with asyncio_patch("gns3server.controller.project.Project.add_node_from_template", return_value={"name": "test", "node_type": "qemu", "compute_id": "example.com"}) as mock: # with asyncio_patch("gns3server.controller.project.Project.add_node_from_template", return_value={"name": "test", "node_type": "qemu", "compute_id": "example.com"}) as mock:
# response = await controller_api.post("/projects/{}/templates/{}".format(project.id, id), { # response = await client.post("/projects/{}/templates/{}".format(project.id, id), {
# "x": 42, # "x": 42,
# "y": 12 # "y": 12
# }) # })
# mock.assert_called_with(id, x=42, y=12, compute_id=None) # mock.assert_called_with(id, x=42, y=12, compute_id=None)
# assert response.status_code == 201 # assert response.status_code == status.HTTP_201_CREATED

View File

@ -0,0 +1,62 @@
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
from fastapi import FastAPI, status
from fastapi.encoders import jsonable_encoder
from httpx import AsyncClient
from gns3server.db.repositories.users import UsersRepository
from gns3server.schemas.users import User
pytestmark = pytest.mark.asyncio
# async def test_route_exist(app: FastAPI, client: AsyncClient) -> None:
#
# params = {"username": "test_username", "email": "user@email.com", "password": "test_password"}
# response = await client.post(app.url_path_for("create_user"), json=params)
# assert response.status_code != status.HTTP_404_NOT_FOUND
#
#
# async def test_users_can_register_successfully(app: FastAPI, client: AsyncClient) -> None:
#
# user_repo = UsersRepository()
# params = {"username": "test_username2", "email": "user2@email.com", "password": "test_password2"}
#
# # make sure the user doesn't exist in the database
# user_in_db = await user_repo.get_user_by_username(params["username"])
# assert user_in_db is None
#
# # register the user
# res = await client.post(app.url_path_for("create_user"), json=params)
# assert res.status_code == status.HTTP_201_CREATED
#
# # make sure the user does exists in the database now
# user_in_db = await user_repo.get_user_by_username(params["username"])
# assert user_in_db is not None
# assert user_in_db.email == params["email"]
# assert user_in_db.username == params["username"]
#
# # check that the user returned in the response is equal to the user in the database
# created_user = User(**res.json()).json()
# print(created_user)
# #print(user_in_db.__dict__)
# test = jsonable_encoder(user_in_db.__dict__, exclude={"_sa_instance_state", "hashed_password"})
# print(test)
# assert created_user == test

View File

@ -17,47 +17,47 @@
import pytest import pytest
from fastapi import FastAPI, status
from httpx import AsyncClient
from gns3server.version import __version__ from gns3server.version import __version__
pytestmark = pytest.mark.asyncio
@pytest.mark.asyncio
async def test_version_output(controller_api, config): async def test_version_output(app: FastAPI, client: AsyncClient, config) -> None:
config.set("Server", "local", "true") config.set("Server", "local", "true")
response = await controller_api.get('/version') response = await client.get(app.url_path_for("get_version"))
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == {'local': True, 'version': __version__} assert response.json() == {'local': True, 'version': __version__}
@pytest.mark.asyncio async def test_version_input(app: FastAPI, client: AsyncClient) -> None:
async def test_version_input(controller_api):
params = {'version': __version__} params = {'version': __version__}
response = await controller_api.post('/version', params) response = await client.post(app.url_path_for("check_version"), json=params)
assert response.status_code == 200 assert response.status_code == status.HTTP_200_OK
assert response.json == {'version': __version__} assert response.json() == {'version': __version__}
@pytest.mark.asyncio async def test_version_invalid_input(app: FastAPI, client: AsyncClient) -> None:
async def test_version_invalid_input(controller_api):
params = {'version': "0.4.2"} params = {'version': "0.4.2"}
response = await controller_api.post('/version', params) response = await client.post(app.url_path_for("check_version"), json=params)
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
assert response.json == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__)} assert response.json() == {'message': 'Client version 0.4.2 is not the same as server version {}'.format(__version__)}
@pytest.mark.asyncio async def test_version_invalid_input_schema(app: FastAPI, client: AsyncClient) -> None:
async def test_version_invalid_input_schema(controller_api):
params = {'version': "0.4.2", "bla": "blu"} params = {'version': "0.4.2", "bla": "blu"}
response = await controller_api.post('/version', params) response = await client.post(app.url_path_for("check_version"), json=params)
assert response.status_code == 409 assert response.status_code == status.HTTP_409_CONFLICT
@pytest.mark.asyncio async def test_version_invalid_json(app: FastAPI, client: AsyncClient) -> None:
async def test_version_invalid_json(controller_api):
params = "BOUM" params = "BOUM"
response = await controller_api.post('/version', params, raw=True) response = await client.post(app.url_path_for("check_version"), json=params)
assert response.status_code == 422 assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY

View File

@ -18,12 +18,16 @@
import pytest import pytest
import os import os
from fastapi import FastAPI, status
from httpx import AsyncClient
from unittest.mock import patch from unittest.mock import patch
from gns3server.version import __version__ from gns3server.version import __version__
from gns3server.controller import Controller from gns3server.controller import Controller
from gns3server.utils.get_resource import get_resource from gns3server.utils.get_resource import get_resource
pytestmark = pytest.mark.asyncio
def get_static(filename): def get_static(filename):
@ -31,15 +35,13 @@ def get_static(filename):
return os.path.join(os.path.abspath(os.path.join(current_dir, '../..', '..', 'gns3server', 'static')), filename) return os.path.join(os.path.abspath(os.path.join(current_dir, '../..', '..', 'gns3server', 'static')), filename)
@pytest.mark.asyncio async def test_debug(app: FastAPI, client: AsyncClient) -> None:
async def test_debug(http_client):
async with http_client as client: response = await client.get(app.url_path_for("debug"))
response = await client.get('/debug') assert response.status_code == status.HTTP_200_OK
assert response.status_code == 200 html = response.read().decode()
html = response.text assert "Website" in html
assert "Website" in html assert __version__ in html
assert __version__ in html
# @pytest.mark.asyncio # @pytest.mark.asyncio
@ -66,20 +68,16 @@ async def test_debug(http_client):
# assert response.status_code == 200 # assert response.status_code == 200
@pytest.mark.asyncio async def test_web_ui(app: FastAPI, client: AsyncClient) -> None:
async def test_web_ui(http_client):
async with http_client as client: response = await client.get(app.url_path_for("web_ui", file_path="index.html"))
response = await client.get('/static/web-ui/index.html') assert response.status_code == status.HTTP_200_OK
assert response.status_code == 200
@pytest.mark.asyncio async def test_web_ui_not_found(app: FastAPI, client: AsyncClient, tmpdir: str) -> None:
async def test_web_ui_not_found(http_client, tmpdir):
with patch('gns3server.utils.get_resource.get_resource') as mock: with patch('gns3server.utils.get_resource.get_resource') as mock:
mock.return_value = str(tmpdir) mock.return_value = str(tmpdir)
async with http_client as client: response = await client.get(app.url_path_for("web_ui", file_path="not-found.txt"))
response = await client.get('/static/web-ui/not-found.txt') # should serve web-ui/index.html
# should serve web-ui/index.html assert response.status_code == status.HTTP_200_OK
assert response.status_code == 200

View File

@ -5,6 +5,10 @@ import shutil
import sys import sys
import os import os
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from asgi_lifespan import LifespanManager
from httpx import AsyncClient
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from pathlib import Path from pathlib import Path
@ -13,16 +17,23 @@ from gns3server.config import Config
from gns3server.compute import MODULES from gns3server.compute import MODULES
from gns3server.compute.port_manager import PortManager from gns3server.compute.port_manager import PortManager
from gns3server.compute.project_manager import ProjectManager from gns3server.compute.project_manager import ProjectManager
from gns3server.db.database import Base
from tests.api.routes.base import Query
sys._called_from_test = True sys._called_from_test = True
sys.original_platform = sys.platform sys.original_platform = sys.platform
from fastapi.testclient import TestClient
from gns3server.api.server import app SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
from httpx import AsyncClient
engine = create_async_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
async def start_db():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
if sys.platform.startswith("win") and sys.version_info < (3, 8): if sys.platform.startswith("win") and sys.version_info < (3, 8):
@ -39,16 +50,39 @@ if sys.platform.startswith("win") and sys.version_info < (3, 8):
asyncio.set_event_loop(None) asyncio.set_event_loop(None)
@pytest.fixture(scope='function') # @pytest.mark.asyncio
def http_client(): # @pytest.fixture(scope="session", autouse=True)
# async def database_connection() -> None:
return AsyncClient(app=app, base_url="http://test-api") #
# from gns3server.db.tasks import connect_to_db
# os.environ["DATABASE_URI"] = "sqlite:///./sql_app_test.db"
# await connect_to_db()
# yield
@pytest.fixture(scope='function') @pytest.fixture#(scope="session")
def ws_client(): async def app() -> FastAPI:
return TestClient(app) from gns3server.api.server import app as gns3_app
gns3_app.add_event_handler("startup", start_db())
return gns3_app
# Grab a reference to our database when needed
#@pytest.fixture
#def db(app: FastAPI) -> Database:
# return app.state._db
@pytest.fixture
async def client(app: FastAPI) -> AsyncClient:
#async with LifespanManager(app):
async with AsyncClient(
app=app,
base_url="http://test-api",
headers={"Content-Type": "application/json"}
) as client:
yield client
@pytest.fixture @pytest.fixture
@ -91,24 +125,6 @@ def compute_project(tmpdir):
return ProjectManager.instance().create_project(project_id="a1e920ca-338a-4e9f-b363-aa607b09dd80") return ProjectManager.instance().create_project(project_id="a1e920ca-338a-4e9f-b363-aa607b09dd80")
@pytest.fixture
def compute_api(http_client, ws_client):
"""
Return an helper allowing you to call the hypervisor API via HTTP
"""
return Query(http_client, ws_client, prefix="/compute", api_version=3)
@pytest.fixture
def controller_api(http_client, ws_client, controller):
"""
Return an helper allowing you to call the server API without any prefix
"""
return Query(http_client, ws_client, api_version=3)
@pytest.fixture @pytest.fixture
def config(): def config():
@ -285,3 +301,4 @@ def run_around_tests(monkeypatch, config, port_manager):#port_manager, controlle
shutil.rmtree(tmppath) shutil.rmtree(tmppath)
except BaseException: except BaseException:
pass pass