1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-13 20:08:55 +00:00

Use dependencies and group common HTTP responses in endpoints.

This commit is contained in:
grossmj 2020-10-14 10:49:29 +10:30
parent 58c5965905
commit 5341ccdbd6
28 changed files with 1376 additions and 1386 deletions

View File

@ -21,16 +21,31 @@ API endpoints for ATM switch nodes.
import os
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.nodes.atm_switch import ATMSwitch
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or ATM switch node"}
}
async def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.ATMSwitch,
@ -54,40 +69,36 @@ async def create_atm_switch(project_id: UUID, node_data: schemas.ATMSwitchCreate
@router.get("/{node_id}",
response_model=schemas.ATMSwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_atm_switch(project_id: UUID, node_id: UUID):
responses=responses)
def get_atm_switch(node: ATMSwitch = Depends(dep_node)):
"""
Return an ATM switch node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node.__json__()
@router.post("/{node_id}/duplicate",
response_model=schemas.ATMSwitch,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_atm_switch(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_atm_switch(destination_node_id: UUID = Body(..., embed=True), node: ATMSwitch = Depends(dep_node)):
"""
Duplicate an ATM switch node.
"""
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.put("/{node_id}",
response_model=schemas.ATMSwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_atm_switch(project_id: UUID, node_id: UUID, node_data: schemas.ATMSwitchUpdate):
responses=responses)
async def update_atm_switch(node_data: schemas.ATMSwitchUpdate, node: ATMSwitch = Depends(dep_node)):
"""
Update an ATM switch node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
if "name" in node_data and node.name != node_data["name"]:
await node.set_name(node_data["name"])
@ -99,94 +110,93 @@ async def update_atm_switch(project_id: UUID, node_id: UUID, node_data: schemas.
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_atm_switch_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_atm_switch_node(node: ATMSwitch = Depends(dep_node)):
"""
Delete an ATM switch node.
"""
dynamips_manager = Dynamips.instance()
await dynamips_manager.delete_node(str(node_id))
await Dynamips.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def start_atm_switch(project_id: UUID, node_id: UUID):
responses=responses)
def start_atm_switch(node: ATMSwitch = Depends(dep_node)):
"""
Start an ATM switch node.
This endpoint results in no action since ATM switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def stop_atm_switch(project_id: UUID, node_id: UUID):
responses=responses)
def stop_atm_switch(node: ATMSwitch = Depends(dep_node)):
"""
Stop an ATM switch node.
This endpoint results in no action since ATM switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def suspend_atm_switch(project_id: UUID, node_id: UUID):
responses=responses)
def suspend_atm_switch(node: ATMSwitch = Depends(dep_node)):
"""
Suspend an ATM switch node.
This endpoint results in no action since ATM switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: ATMSwitch = Depends(dep_node)):
"""
Add a NIO (Network Input/Output) to the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
await node.add_nio(nio, port_number)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: ATMSwitch = Depends(dep_node)):
"""
Remove a NIO (Network Input/Output) from the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await node.remove_nio(port_number)
await nio.delete()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: ATMSwitch = Depends(dep_node)):
"""
Start a packet capture on the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
@ -194,28 +204,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: ATMSwitch = Depends(dep_node)):
"""
Stop a packet capture on the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await node.stop_capture(port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: ATMSwitch = Depends(dep_node)):
"""
Stream the pcap capture file.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -21,7 +21,7 @@ API endpoints for cloud nodes.
import os
from fastapi import APIRouter, status
from fastapi import APIRouter, Depends, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from typing import Union
@ -29,9 +29,24 @@ from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.builtin import Builtin
from gns3server.compute.builtin.nodes.cloud import Cloud
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or cloud node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.Cloud,
@ -61,27 +76,23 @@ async def create_cloud(project_id: UUID, node_data: schemas.CloudCreate):
@router.get("/{node_id}",
response_model=schemas.Cloud,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_cloud(project_id: UUID, node_id: UUID):
responses=responses)
def get_cloud(node: Cloud = Depends(dep_node)):
"""
Return a cloud node.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.Cloud,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def update_cloud(project_id: UUID, node_id: UUID, node_data: schemas.CloudUpdate):
responses=responses)
def update_cloud(node_data: schemas.CloudUpdate, node: Cloud = Depends(dep_node)):
"""
Update a cloud node.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
for name, value in node_data.items():
if hasattr(node, name) and getattr(node, name) != value:
@ -92,69 +103,64 @@ def update_cloud(project_id: UUID, node_id: UUID, node_data: schemas.CloudUpdate
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_node(node: Cloud = Depends(dep_node)):
"""
Delete a cloud node.
"""
builtin_manager = Builtin.instance()
await builtin_manager.delete_node(str(node_id))
await Builtin.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_cloud(project_id: UUID, node_id: UUID):
responses=responses)
async def start_cloud(node: Cloud = Depends(dep_node)):
"""
Start a cloud node.
"""
node = Builtin.instance().get_node(str(node_id), project_id=str(project_id))
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_cloud(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_cloud(node: Cloud = Depends(dep_node)):
"""
Stop a cloud node.
This endpoint results in no action since cloud nodes cannot be stopped.
"""
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_cloud(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_cloud(node: Cloud = Depends(dep_node)):
"""
Suspend a cloud node.
This endpoint results in no action since cloud nodes cannot be suspended.
"""
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID,
node_id: UUID,
adapter_number: int,
responses=responses)
async def create_nio(adapter_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)):
"""
Add a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
nio = builtin_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
nio = Builtin.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.add_nio(nio, port_number)
return nio.__json__()
@ -162,19 +168,16 @@ async def create_nio(project_id: UUID,
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID,
node_id: UUID,
adapter_number: int,
responses=responses)
async def update_nio(adapter_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)):
"""
Update a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
if nio_data.filters:
nio.filters = nio_data.filters
@ -184,28 +187,27 @@ async def update_nio(project_id: UUID,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
"""
Remove a NIO (Network Input/Output) from the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
await node.remove_nio(port_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: Cloud = Depends(dep_node)):
"""
Start a packet capture on the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
@ -213,28 +215,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
"""
Stop a packet capture on the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
await node.stop_capture(port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
"""
Stream the pcap capture file.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
stream = builtin_manager.stream_pcap_file(nio, node.project.id)
stream = Builtin.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -124,13 +124,13 @@ def statistics() -> dict:
@router.get("/qemu/binaries")
async def list_binaries(archs: Optional[List[str]] = Body(None, embed=True)):
async def get_binaries(archs: Optional[List[str]] = Body(None, embed=True)):
return await Qemu.binary_list(archs)
@router.get("/qemu/img-binaries")
async def list_img_binaries():
async def get_img_binaries():
return await Qemu.img_binary_list()

View File

@ -21,16 +21,31 @@ API endpoints for Docker nodes.
import os
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.docker import Docker
from gns3server.compute.docker.docker_vm import DockerVM
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Docker node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
docker_manager = Docker.instance()
node = docker_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.Docker,
@ -71,28 +86,23 @@ async def create_docker_node(project_id: UUID, node_data: schemas.DockerCreate):
@router.get("/{node_id}",
response_model=schemas.Docker,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
def get_docker_node(node: DockerVM = Depends(dep_node)):
"""
Return a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
return container.__json__()
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.Docker,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_docker(project_id: UUID, node_id: UUID, node_data: schemas.DockerUpdate):
responses=responses)
async def update_docker(node_data: schemas.DockerUpdate, node: DockerVM = Depends(dep_node)):
"""
Update a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
props = [
"name", "console", "console_type", "aux", "aux_type", "console_resolution",
"console_http_port", "console_http_path", "start_command",
@ -103,222 +113,201 @@ async def update_docker(project_id: UUID, node_id: UUID, node_data: schemas.Dock
changed = False
node_data = jsonable_encoder(node_data, exclude_unset=True)
for prop in props:
if prop in node_data and node_data[prop] != getattr(container, prop):
setattr(container, prop, node_data[prop])
if prop in node_data and node_data[prop] != getattr(node, prop):
setattr(node, prop, node_data[prop])
changed = True
# We don't call container.update for nothing because it will restart the container
if changed:
await container.update()
container.updated()
return container.__json__()
await node.update()
node.updated()
return node.__json__()
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
async def start_docker_node(node: DockerVM = Depends(dep_node)):
"""
Start a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.start()
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_docker_node(node: DockerVM = Depends(dep_node)):
"""
Stop a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.stop()
await node.stop()
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_docker_node(node: DockerVM = Depends(dep_node)):
"""
Suspend a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.pause()
await node.pause()
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reload_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_docker_node(node: DockerVM = Depends(dep_node)):
"""
Reload a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.restart()
await node.restart()
@router.post("/{node_id}/pause",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def pause_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
async def pause_docker_node(node: DockerVM = Depends(dep_node)):
"""
Pause a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.pause()
await node.pause()
@router.post("/{node_id}/unpause",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def unpause_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
async def unpause_docker_node(node: DockerVM = Depends(dep_node)):
"""
Unpause a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.unpause()
await node.unpause()
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_docker_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_docker_node(node: DockerVM = Depends(dep_node)):
"""
Delete a Docker node.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.delete()
await node.delete()
@router.post("/{node_id}/duplicate",
response_model=schemas.Docker,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_docker_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_docker_node(destination_node_id: UUID = Body(..., embed=True), node: DockerVM = Depends(dep_node)):
"""
Duplicate a Docker node.
"""
docker_manager = Docker.instance()
new_node = await docker_manager.duplicate_node(str(node_id), str(destination_node_id))
new_node = await Docker.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: DockerVM = Depends(dep_node)):
"""
Add a NIO (Network Input/Output) to the node.
The port number on the Docker node is always 0.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
nio = docker_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await container.adapter_add_nio_binding(adapter_number, nio)
nio = Docker.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.adapter_add_nio_binding(adapter_number, nio)
return nio.__json__()
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def update_nio(adapter_number: int,
port_number: int, nio_data: schemas.UDPNIO,
node: DockerVM = Depends(dep_node)):
"""
Update a NIO (Network Input/Output) on the node.
The port number on the Docker node is always 0.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
nio = container.get_nio(adapter_number)
nio = node.get_nio(adapter_number)
if nio_data.filters:
nio.filters = nio_data.filters
await container.adapter_update_nio_binding(adapter_number, nio)
await node.adapter_update_nio_binding(adapter_number, nio)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
The port number on the Docker node is always 0.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.adapter_remove_nio_binding(adapter_number)
await node.adapter_remove_nio_binding(adapter_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: DockerVM = Depends(dep_node)):
"""
Start a packet capture on the node.
The port number on the Docker node is always 0.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(container.project.capture_working_directory(), node_capture_data.capture_file_name)
await container.start_capture(adapter_number, pcap_file_path)
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(adapter_number, pcap_file_path)
return {"pcap_file_path": str(pcap_file_path)}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
"""
Stop a packet capture on the node.
The port number on the Docker node is always 0.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.stop_capture(adapter_number)
await node.stop_capture(adapter_number)
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reset_console(project_id: UUID, node_id: UUID):
responses=responses)
async def reset_console(node: DockerVM = Depends(dep_node)):
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
await container.reset_console()
await node.reset_console()
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
"""
Stream the pcap capture file.
The port number on the Docker node is always 0.
"""
docker_manager = Docker.instance()
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
nio = container.get_nio(adapter_number)
stream = docker_manager.stream_pcap_file(nio, container.project.id)
nio = node.get_nio(adapter_number)
stream = Docker.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
# @Route.get(

View File

@ -22,19 +22,23 @@ API endpoints for Dynamips nodes.
import os
import sys
from fastapi import APIRouter, status
from fastapi import APIRouter, Depends, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from typing import List
from uuid import UUID
from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.nodes.router import Router
from gns3server.compute.dynamips.dynamips_error import DynamipsError
from gns3server.compute.project_manager import ProjectManager
from gns3server.endpoints import schemas
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Dynamips node"}
}
DEFAULT_CHASSIS = {
"c1700": "1720",
"c2600": "2610",
@ -42,6 +46,16 @@ DEFAULT_CHASSIS = {
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.Dynamips,
status_code=status.HTTP_201_CREATED,
@ -74,169 +88,148 @@ async def create_router(project_id: UUID, node_data: schemas.DynamipsCreate):
@router.get("/{node_id}",
response_model=schemas.Dynamips,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_router(project_id: UUID, node_id: UUID):
responses=responses)
def get_router(node: Router = Depends(dep_node)):
"""
Return Dynamips router.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return vm.__json__()
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.Dynamips,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_router(project_id: UUID, node_id: UUID, node_data: schemas.DynamipsUpdate):
responses=responses)
async def update_router(node_data: schemas.DynamipsUpdate, node: Router = Depends(dep_node)):
"""
Update a Dynamips router.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await dynamips_manager.update_vm_settings(vm, jsonable_encoder(node_data, exclude_unset=True))
vm.updated()
return vm.__json__()
await Dynamips.instance().update_vm_settings(node, jsonable_encoder(node_data, exclude_unset=True))
node.updated()
return node.__json__()
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_router(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_router(node: Router = Depends(dep_node)):
"""
Delete a Dynamips router.
"""
# check the project_id exists
ProjectManager.instance().get_project(str(project_id))
await Dynamips.instance().delete_node(str(node_id))
await Dynamips.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_router(project_id: UUID, node_id: UUID):
responses=responses)
async def start_router(node: Router = Depends(dep_node)):
"""
Start a Dynamips router.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
try:
await dynamips_manager.ghost_ios_support(vm)
await Dynamips.instance().ghost_ios_support(node)
except GeneratorExit:
pass
await vm.start()
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_router(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_router(node: Router = Depends(dep_node)):
"""
Stop a Dynamips router.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop()
await node.stop()
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_router(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_router(node: Router = Depends(dep_node)):
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await vm.suspend()
await node.suspend()
@router.post("/{node_id}/resume",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def resume_router(project_id: UUID, node_id: UUID):
responses=responses)
async def resume_router(node: Router = Depends(dep_node)):
"""
Resume a suspended Dynamips router.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await vm.resume()
await node.resume()
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reload(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_router(node: Router = Depends(dep_node)):
"""
Reload a suspended Dynamips router.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reload()
await node.reload()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: Router = Depends(dep_node)):
"""
Add a NIO (Network Input/Output) to the node.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await dynamips_manager.create_nio(vm, jsonable_encoder(nio_data, exclude_unset=True))
await vm.slot_add_nio_binding(adapter_number, port_number, nio)
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
await node.slot_add_nio_binding(adapter_number, port_number, nio)
return nio.__json__()
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: Router = Depends(dep_node)):
"""
Update a NIO (Network Input/Output) on the node.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number, port_number)
nio = node.get_nio(adapter_number, port_number)
if nio_data.filters:
nio.filters = nio_data.filters
await vm.slot_update_nio_binding(adapter_number, port_number, nio)
await node.slot_update_nio_binding(adapter_number, port_number, nio)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: Router = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await vm.slot_remove_nio_binding(adapter_number, port_number)
nio = await node.slot_remove_nio_binding(adapter_number, port_number)
await nio.delete()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: Router = Depends(dep_node)):
"""
Start a packet capture on the node.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
if sys.platform.startswith('win'):
# FIXME: Dynamips (Cygwin actually) doesn't like non ascii paths on Windows
@ -245,72 +238,64 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
except UnicodeEncodeError:
raise DynamipsError('The capture file path "{}" must only contain ASCII (English) characters'.format(pcap_file_path))
await vm.start_capture(adapter_number, port_number, pcap_file_path, node_capture_data.data_link_type)
await node.start_capture(adapter_number, port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: Router = Depends(dep_node)):
"""
Stop a packet capture on the node.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop_capture(adapter_number, port_number)
await node.stop_capture(adapter_number, port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: Router = Depends(dep_node)):
"""
Stream the pcap capture file.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number, port_number)
stream = dynamips_manager.stream_pcap_file(nio, vm.project.id)
nio = node.get_nio(adapter_number, port_number)
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
@router.get("/{node_id}/idlepc_proposals",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def get_idlepcs(project_id: UUID, node_id: UUID) -> List[str]:
responses=responses)
async def get_idlepcs(node: Router = Depends(dep_node)) -> List[str]:
"""
Retrieve Dynamips idle-pc proposals
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await vm.set_idlepc("0x0")
return await vm.get_idle_pc_prop()
await node.set_idlepc("0x0")
return await node.get_idle_pc_prop()
@router.get("/{node_id}/auto_idlepc",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def get_auto_idlepc(project_id: UUID, node_id: UUID) -> dict:
responses=responses)
async def get_auto_idlepc(node: Router = Depends(dep_node)) -> dict:
"""
Get an automatically guessed best idle-pc value.
"""
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
idlepc = await dynamips_manager.auto_idlepc(vm)
idlepc = await Dynamips.instance().auto_idlepc(node)
return {"idlepc": idlepc}
@router.post("/{node_id}/duplicate",
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_router(project_id: UUID, node_id: UUID, destination_node_id: UUID):
responses=responses)
async def duplicate_router(destination_node_id: UUID, node: Router = Depends(dep_node)):
"""
Duplicate a router.
"""
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@ -330,9 +315,7 @@ async def duplicate_router(project_id: UUID, node_id: UUID, destination_node_id:
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reset_console(project_id: UUID, node_id: UUID):
responses=responses)
async def reset_console(node: Router = Depends(dep_node)):
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reset_console()
await node.reset_console()

View File

@ -21,16 +21,31 @@ API endpoints for Ethernet hub nodes.
import os
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.nodes.ethernet_hub import EthernetHub
from gns3server.endpoints import schemas
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Ethernet hub node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.EthernetHub,
@ -54,40 +69,37 @@ async def create_ethernet_hub(project_id: UUID, node_data: schemas.EthernetHubCr
@router.get("/{node_id}",
response_model=schemas.EthernetHub,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_ethernet_hub(project_id: UUID, node_id: UUID):
responses=responses)
def get_ethernet_hub(node: EthernetHub = Depends(dep_node)):
"""
Return an Ethernet hub.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node.__json__()
@router.post("/{node_id}/duplicate",
response_model=schemas.EthernetHub,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_ethernet_hub(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_ethernet_hub(destination_node_id: UUID = Body(..., embed=True),
node: EthernetHub = Depends(dep_node)):
"""
Duplicate an Ethernet hub.
"""
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.put("/{node_id}",
response_model=schemas.EthernetHub,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_ethernet_hub(project_id: UUID, node_id: UUID, node_data: schemas.EthernetHubUpdate):
responses=responses)
async def update_ethernet_hub(node_data: schemas.EthernetHubUpdate, node: EthernetHub = Depends(dep_node)):
"""
Update an Ethernet hub.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
if "name" in node_data and node.name != node_data["name"]:
await node.set_name(node_data["name"])
@ -99,94 +111,93 @@ async def update_ethernet_hub(project_id: UUID, node_id: UUID, node_data: schema
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_ethernet_hub(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_ethernet_hub(node: EthernetHub = Depends(dep_node)):
"""
Delete an Ethernet hub.
"""
dynamips_manager = Dynamips.instance()
await dynamips_manager.delete_node(str(node_id))
await Dynamips.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def start_ethernet_hub(project_id: UUID, node_id: UUID):
responses=responses)
def start_ethernet_hub(node: EthernetHub = Depends(dep_node)):
"""
Start an Ethernet hub.
This endpoint results in no action since Ethernet hub nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def stop_ethernet_hub(project_id: UUID, node_id: UUID):
responses=responses)
def stop_ethernet_hub(node: EthernetHub = Depends(dep_node)):
"""
Stop an Ethernet hub.
This endpoint results in no action since Ethernet hub nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def suspend_ethernet_hub(project_id: UUID, node_id: UUID):
responses=responses)
def suspend_ethernet_hub(node: EthernetHub = Depends(dep_node)):
"""
Suspend an Ethernet hub.
This endpoint results in no action since Ethernet hub nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: EthernetHub = Depends(dep_node)):
"""
Add a NIO (Network Input/Output) to the node.
The adapter number on the hub is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
await node.add_nio(nio, port_number)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: EthernetHub = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
The adapter number on the hub is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await node.remove_nio(port_number)
await nio.delete()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: EthernetHub = Depends(dep_node)):
"""
Start a packet capture on the node.
The adapter number on the hub is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
@ -194,29 +205,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: EthernetHub = Depends(dep_node)):
"""
Stop a packet capture on the node.
The adapter number on the hub is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await node.stop_capture(port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: EthernetHub = Depends(dep_node)):
"""
Stream the pcap capture file.
The adapter number on the hub is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -21,21 +21,36 @@ API endpoints for Ethernet switch nodes.
import os
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.nodes.ethernet_switch import EthernetSwitch
from gns3server.endpoints import schemas
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Ethernet switch node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.EthernetSwitch,
status_code=status.HTTP_201_CREATED,
responses={409: {"model": schemas.ErrorMessage, "description": "Could not create Ethernet hub node"}})
responses={409: {"model": schemas.ErrorMessage, "description": "Could not create Ethernet switch node"}})
async def create_ethernet_switch(project_id: UUID, node_data: schemas.EthernetSwitchCreate):
"""
Create a new Ethernet switch.
@ -57,37 +72,34 @@ async def create_ethernet_switch(project_id: UUID, node_data: schemas.EthernetSw
@router.get("/{node_id}",
response_model=schemas.EthernetSwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_ethernet_switch(project_id: UUID, node_id: UUID):
responses=responses)
def get_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node.__json__()
@router.post("/{node_id}/duplicate",
response_model=schemas.EthernetSwitch,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_ethernet_switch(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_ethernet_switch(destination_node_id: UUID = Body(..., embed=True),
node: EthernetSwitch = Depends(dep_node)):
"""
Duplicate an Ethernet switch.
"""
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.put("/{node_id}",
response_model=schemas.EthernetSwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_ethernet_switch(project_id: UUID, node_id: UUID, node_data: schemas.EthernetSwitchUpdate):
responses=responses)
async def update_ethernet_switch(node_data: schemas.EthernetSwitchUpdate, node: EthernetSwitch = Depends(dep_node)):
"""
Update an Ethernet switch.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
if "name" in node_data and node.name != node_data["name"]:
await node.set_name(node_data["name"])
@ -102,119 +114,114 @@ async def update_ethernet_switch(project_id: UUID, node_id: UUID, node_data: sch
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_ethernet_switch(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
"""
Delete an Ethernet switch.
"""
dynamips_manager = Dynamips.instance()
await dynamips_manager.delete_node(str(node_id))
await Dynamips.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def start_ethernet_switch(project_id: UUID, node_id: UUID):
responses=responses)
def start_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
"""
Start an Ethernet switch.
This endpoint results in no action since Ethernet switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def stop_ethernet_switch(project_id: UUID, node_id: UUID):
responses=responses)
def stop_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
"""
Stop an Ethernet switch.
This endpoint results in no action since Ethernet switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def suspend(project_id: UUID, node_id: UUID):
responses=responses)
def suspend_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
"""
Suspend an Ethernet switch.
This endpoint results in no action since Ethernet switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: EthernetSwitch = Depends(dep_node)):
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
await node.add_nio(nio, port_number)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: EthernetSwitch = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await node.remove_nio(port_number)
await nio.delete()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: EthernetSwitch = Depends(dep_node)):
"""
Start a packet capture on the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int,port_number: int, node: EthernetSwitch = Depends(dep_node)):
"""
Stop a packet capture on the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await node.stop_capture(port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: EthernetSwitch = Depends(dep_node)):
"""
Stream the pcap capture file.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -21,16 +21,31 @@ API endpoints for Frame Relay switch nodes.
import os
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.nodes.frame_relay_switch import FrameRelaySwitch
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Frame Relay switch node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.FrameRelaySwitch,
@ -54,40 +69,38 @@ async def create_frame_relay_switch(project_id: UUID, node_data: schemas.FrameRe
@router.get("/{node_id}",
response_model=schemas.FrameRelaySwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_frame_relay_switch(project_id: UUID, node_id: UUID):
responses=responses)
def get_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
"""
Return a Frame Relay switch node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
return node.__json__()
@router.post("/{node_id}/duplicate",
response_model=schemas.FrameRelaySwitch,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_frame_relay_switch(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_frame_relay_switch(destination_node_id: UUID = Body(..., embed=True),
node: FrameRelaySwitch = Depends(dep_node)):
"""
Duplicate a Frame Relay switch node.
"""
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.put("/{node_id}",
response_model=schemas.FrameRelaySwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_frame_relay_switch(project_id: UUID, node_id: UUID, node_data: schemas.FrameRelaySwitchUpdate):
responses=responses)
async def update_frame_relay_switch(node_data: schemas.FrameRelaySwitchUpdate,
node: FrameRelaySwitch = Depends(dep_node)):
"""
Update an Frame Relay switch node.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
if "name" in node_data and node.name != node_data["name"]:
await node.set_name(node_data["name"])
@ -99,94 +112,93 @@ async def update_frame_relay_switch(project_id: UUID, node_id: UUID, node_data:
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_frame_relay_switch_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
"""
Delete a Frame Relay switch node.
"""
dynamips_manager = Dynamips.instance()
await dynamips_manager.delete_node(str(node_id))
await Dynamips.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def start_frame_relay_switch(project_id: UUID, node_id: UUID):
responses=responses)
def start_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
"""
Start a Frame Relay switch node.
This endpoint results in no action since Frame Relay switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def stop_frame_relay_switch(project_id: UUID, node_id: UUID):
responses=responses)
def stop_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
"""
Stop a Frame Relay switch node.
This endpoint results in no action since Frame Relay switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def suspend_frame_relay_switch(project_id: UUID, node_id: UUID):
responses=responses)
def suspend_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
"""
Suspend a Frame Relay switch node.
This endpoint results in no action since Frame Relay switch nodes are always on.
"""
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: FrameRelaySwitch = Depends(dep_node)):
"""
Add a NIO (Network Input/Output) to the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
await node.add_nio(nio, port_number)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
"""
Remove a NIO (Network Input/Output) from the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = await node.remove_nio(port_number)
await nio.delete()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: FrameRelaySwitch = Depends(dep_node)):
"""
Start a packet capture on the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
@ -194,28 +206,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
"""
Stop a packet capture on the node.
The adapter number on the switch is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
await node.stop_capture(port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
"""
Stream the pcap capture file.
The adapter number on the hub is always 0.
"""
dynamips_manager = Dynamips.instance()
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -125,7 +125,7 @@ async def download_iou_image(filename: str):
@router.get("/qemu/images")
async def list_qemu_images() -> List[str]:
async def get_qemu_images() -> List[str]:
qemu_manager = Qemu.instance()
return await qemu_manager.list_images()

View File

@ -21,7 +21,7 @@ API endpoints for IOU nodes.
import os
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from typing import Union
@ -29,9 +29,24 @@ from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.iou import IOU
from gns3server.compute.iou.iou_vm import IOUVM
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or IOU node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
iou_manager = IOU.instance()
node = iou_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.IOU,
@ -68,229 +83,204 @@ async def create_iou_node(project_id: UUID, node_data: schemas.IOUCreate):
@router.get("/{node_id}",
response_model=schemas.IOU,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_iou_node(project_id: UUID, node_id: UUID):
responses=responses)
def get_iou_node(node: IOUVM = Depends(dep_node)):
"""
Return an IOU node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
return vm.__json__()
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.IOU,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_iou_node(project_id: UUID, node_id: UUID, node_data: schemas.IOUUpdate):
responses=responses)
async def update_iou_node(node_data: schemas.IOUUpdate, node: IOUVM = Depends(dep_node)):
"""
Update an IOU node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
for name, value in node_data.items():
if hasattr(vm, name) and getattr(vm, name) != value:
if hasattr(node, name) and getattr(node, name) != value:
if name == "application_id":
continue # we must ignore this to avoid overwriting the application_id allocated by the IOU manager
setattr(vm, name, value)
setattr(node, name, value)
if vm.use_default_iou_values:
if node.use_default_iou_values:
# update the default IOU values in case the image or use_default_iou_values have changed
# this is important to have the correct NVRAM amount in order to correctly push the configs to the NVRAM
await vm.update_default_iou_values()
vm.updated()
return vm.__json__()
await node.update_default_iou_values()
node.updated()
return node.__json__()
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_iou_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_iou_node(node: IOUVM = Depends(dep_node)):
"""
Delete an IOU node.
"""
await IOU.instance().delete_node(str(node_id))
await IOU.instance().delete_node(node.id)
@router.post("/{node_id}/duplicate",
response_model=schemas.IOU,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_iou_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_iou_node(destination_node_id: UUID = Body(..., embed=True), node: IOUVM = Depends(dep_node)):
"""
Duplicate an IOU node.
"""
new_node = await IOU.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await IOU.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_iou_node(project_id: UUID, node_id: UUID, start_data: schemas.IOUStart):
responses=responses)
async def start_iou_node(start_data: schemas.IOUStart, node: IOUVM = Depends(dep_node)):
"""
Start an IOU node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
start_data = jsonable_encoder(start_data, exclude_unset=True)
for name, value in start_data.items():
if hasattr(vm, name) and getattr(vm, name) != value:
setattr(vm, name, value)
if hasattr(node, name) and getattr(node, name) != value:
setattr(node, name, value)
await vm.start()
return vm.__json__()
await node.start()
return node.__json__()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop(project_id: UUID, node_id: UUID):
responses=responses)
async def stop(node: IOUVM = Depends(dep_node)):
"""
Stop an IOU node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop()
await node.stop()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def suspend_iou_node(project_id: UUID, node_id: UUID):
responses=responses)
def suspend_iou_node(node: IOUVM = Depends(dep_node)):
"""
Suspend an IOU node.
Does nothing since IOU doesn't support being suspended.
"""
iou_manager = IOU.instance()
iou_manager.get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reload_iou_node(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_iou_node(node: IOUVM = Depends(dep_node)):
"""
Reload an IOU node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reload()
await node.reload()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID,
node_id: UUID,
adapter_number: int,
responses=responses)
async def create_nio(adapter_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)):
"""
Add a NIO (Network Input/Output) to the node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
nio = iou_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await vm.adapter_add_nio_binding(adapter_number, port_number, nio)
nio = IOU.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.adapter_add_nio_binding(adapter_number, port_number, nio)
return nio.__json__()
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID,
node_id: UUID,
adapter_number: int,
responses=responses)
async def update_nio(adapter_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)):
"""
Update a NIO (Network Input/Output) on the node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number, port_number)
nio = node.get_nio(adapter_number, port_number)
if nio_data.filters:
nio.filters = nio_data.filters
await vm.adapter_update_nio_binding(adapter_number, port_number, nio)
return nio.__json__()
await node.adapter_update_nio_binding(adapter_number, port_number, nio)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
await vm.adapter_remove_nio_binding(adapter_number, port_number)
await node.adapter_remove_nio_binding(adapter_number, port_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: IOUVM = Depends(dep_node)):
"""
Start a packet capture on the node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
await vm.start_capture(adapter_number, pcap_file_path)
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(adapter_number, pcap_file_path)
return {"pcap_file_path": str(pcap_file_path)}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
"""
Stop a packet capture on the node.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop_capture(adapter_number, port_number)
await node.stop_capture(adapter_number, port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
"""
Stream the pcap capture file.
"""
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number, port_number)
stream = iou_manager.stream_pcap_file(nio, vm.project.id)
nio = node.get_nio(adapter_number, port_number)
stream = IOU.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reset_console(project_id: UUID, node_id: UUID):
responses=responses)
async def reset_console(node: IOUVM = Depends(dep_node)):
iou_manager = IOU.instance()
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reset_console()
await node.reset_console()
# @Route.get(

View File

@ -21,7 +21,7 @@ API endpoints for NAT nodes.
import os
from fastapi import APIRouter, status
from fastapi import APIRouter, Depends, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from typing import Union
@ -29,9 +29,24 @@ from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.builtin import Builtin
from gns3server.compute.builtin.nodes.nat import Nat
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or NAT node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.NAT,
@ -56,27 +71,23 @@ async def create_nat(project_id: UUID, node_data: schemas.NATCreate):
@router.get("/{node_id}",
response_model=schemas.NAT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_nat(project_id: UUID, node_id: UUID):
responses=responses)
def get_nat(node: Nat = Depends(dep_node)):
"""
Return a NAT node.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.NAT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def update_nat(project_id: UUID, node_id: UUID, node_data: schemas.NATUpdate):
responses=responses)
def update_nat(node_data: schemas.NATUpdate, node: Nat = Depends(dep_node)):
"""
Update a NAT node.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
for name, value in node_data.items():
if hasattr(node, name) and getattr(node, name) != value:
@ -87,69 +98,64 @@ def update_nat(project_id: UUID, node_id: UUID, node_data: schemas.NATUpdate):
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nat(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_nat(node: Nat = Depends(dep_node)):
"""
Delete a cloud node.
"""
builtin_manager = Builtin.instance()
await builtin_manager.delete_node(str(node_id))
await Builtin.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_nat(project_id: UUID, node_id: UUID):
responses=responses)
async def start_nat(node: Nat = Depends(dep_node)):
"""
Start a NAT node.
"""
node = Builtin.instance().get_node(str(node_id), project_id=str(project_id))
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_nat(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_nat(node: Nat = Depends(dep_node)):
"""
Stop a NAT node.
This endpoint results in no action since cloud nodes cannot be stopped.
"""
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_nat(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_nat(node: Nat = Depends(dep_node)):
"""
Suspend a NAT node.
This endpoint results in no action since NAT nodes cannot be suspended.
"""
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID,
node_id: UUID,
adapter_number: int,
responses=responses)
async def create_nio(adapter_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)):
"""
Add a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
nio = builtin_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
nio = Builtin.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.add_nio(nio, port_number)
return nio.__json__()
@ -157,19 +163,16 @@ async def create_nio(project_id: UUID,
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID,
node_id: UUID,
adapter_number: int,
responses=responses)
async def update_nio(adapter_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)):
"""
Update a NIO (Network Input/Output) to the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
if nio_data.filters:
nio.filters = nio_data.filters
@ -179,28 +182,27 @@ async def update_nio(project_id: UUID,
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
"""
Remove a NIO (Network Input/Output) from the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
await node.remove_nio(port_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: Nat = Depends(dep_node)):
"""
Start a packet capture on the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path}
@ -208,28 +210,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
"""
Stop a packet capture on the node.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
await node.stop_capture(port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
"""
Stream the pcap capture file.
The adapter number on the cloud is always 0.
"""
builtin_manager = Builtin.instance()
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
nio = node.get_nio(port_number)
stream = builtin_manager.stream_pcap_file(nio, node.project.id)
stream = Builtin.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -20,30 +20,39 @@ API endpoints for projects.
"""
import shutil
import aiohttp
import os
from fastapi import APIRouter, HTTPException, Request, status
import logging
log = logging.getLogger()
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import FileResponse
from typing import List
from uuid import UUID
from gns3server.compute.project_manager import ProjectManager
from gns3server.compute.project import Project
from gns3server.endpoints import schemas
router = APIRouter()
import aiohttp
import os
from gns3server.compute.project_manager import ProjectManager
import logging
log = logging.getLogger()
# How many clients have subscribed to notifications
_notifications_listening = {}
def dep_project(project_id: UUID):
"""
Dependency to retrieve a project.
"""
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
return project
@router.get("/projects", response_model=List[schemas.Project])
def get_projects():
"""
@ -73,41 +82,35 @@ def create_project(project_data: schemas.ProjectCreate):
@router.put("/projects/{project_id}",
response_model=schemas.Project)
async def update_project(project_id: UUID, project_data: schemas.ProjectUpdate):
async def update_project(project_data: schemas.ProjectUpdate, project: Project = Depends(dep_project)):
"""
Update project on the compute.
"""
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
await project.update(variables=project_data.variables)
return project.__json__()
@router.get("/projects/{project_id}",
response_model=schemas.Project)
def get_project(project_id: UUID):
def get_project(project: Project = Depends(dep_project)):
"""
Return a project from the compute.
"""
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
return project.__json__()
@router.post("/projects/{project_id}/close",
status_code=status.HTTP_204_NO_CONTENT)
async def close_project(project_id: UUID):
async def close_project(project: Project = Depends(dep_project)):
"""
Close a project on the compute.
"""
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
if _notifications_listening.setdefault(project.id, 0) <= 1:
await project.close()
pm.remove_project(project.id)
ProjectManager.instance().remove_project(project.id)
try:
del _notifications_listening[project.id]
except KeyError:
@ -118,15 +121,13 @@ async def close_project(project_id: UUID):
@router.delete("/projects/{project_id}",
status_code=status.HTTP_204_NO_CONTENT)
async def delete_project(project_id: UUID):
async def delete_project(project: Project = Depends(dep_project)):
"""
Delete project from the compute.
"""
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
await project.delete()
pm.remove_project(project.id)
ProjectManager.instance().remove_project(project.id)
# @Route.get(
# r"/projects/{project_id}/notifications",
@ -184,24 +185,20 @@ async def delete_project(project_id: UUID):
@router.get("/projects/{project_id}/files",
response_model=List[schemas.ProjectFile])
async def get_project_files(project_id: UUID):
async def get_project_files(project: Project = Depends(dep_project)):
"""
Return files belonging to a project.
"""
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
return await project.list_files()
@router.get("/projects/{project_id}/files/{file_path:path}")
async def get_file(project_id: UUID, file_path: str):
async def get_file(file_path: str, project: Project = Depends(dep_project)):
"""
Get a file from a project.
"""
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
path = os.path.normpath(file_path)
# Raise error if user try to escape
@ -217,10 +214,8 @@ async def get_file(project_id: UUID, file_path: str):
@router.post("/projects/{project_id}/files/{file_path:path}",
status_code=status.HTTP_204_NO_CONTENT)
async def write_file(project_id: UUID, file_path: str, request: Request):
async def write_file(file_path: str, request: Request, project: Project = Depends(dep_project)):
pm = ProjectManager.instance()
project = pm.get_project(str(project_id))
path = os.path.normpath(file_path)
# Raise error if user try to escape

View File

@ -22,7 +22,7 @@ API endpoints for Qemu nodes.
import os
import sys
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
@ -31,9 +31,24 @@ from gns3server.endpoints import schemas
from gns3server.compute.project_manager import ProjectManager
from gns3server.compute.compute_error import ComputeError
from gns3server.compute.qemu import Qemu
from gns3server.compute.qemu.qemu_vm import QemuVM
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Qemu node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
qemu_manager = Qemu.instance()
node = qemu_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.Qemu,
@ -66,250 +81,224 @@ async def create_qemu_node(project_id: UUID, node_data: schemas.QemuCreate):
@router.get("/{node_id}",
response_model=schemas.Qemu,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_qemu_node(project_id: UUID, node_id: UUID):
responses=responses)
def get_qemu_node(node: QemuVM = Depends(dep_node)):
"""
Return a Qemu node.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
return vm.__json__()
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.Qemu,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_qemu_node(project_id: UUID, node_id: UUID, node_data: schemas.QemuUpdate):
responses=responses)
async def update_qemu_node(node_data: schemas.QemuUpdate, node: QemuVM = Depends(dep_node)):
"""
Update a Qemu node.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
# update the console first to avoid issue if updating console type
vm.console = node_data.pop("console", vm.console)
node.console = node_data.pop("console", node.console)
for name, value in node_data.items():
if hasattr(vm, name) and getattr(vm, name) != value:
await vm.update_property(name, value)
vm.updated()
return vm.__json__()
if hasattr(node, name) and getattr(node, name) != value:
await node.update_property(name, value)
node.updated()
return node.__json__()
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_qemu_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_qemu_node(node: QemuVM = Depends(dep_node)):
"""
Delete a Qemu node.
"""
await Qemu.instance().delete_node(str(node_id))
await Qemu.instance().delete_node(node.id)
@router.post("/{node_id}/duplicate",
response_model=schemas.Qemu,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_qemu_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_qemu_node(destination_node_id: UUID = Body(..., embed=True), node: QemuVM = Depends(dep_node)):
"""
Duplicate a Qemu node.
"""
new_node = await Qemu.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await Qemu.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.post("/{node_id}/resize_disk",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def resize_qemu_node_disk(project_id: UUID, node_id: UUID, node_data: schemas.QemuDiskResize):
responses=responses)
async def resize_qemu_node_disk(node_data: schemas.QemuDiskResize, node: QemuVM = Depends(dep_node)):
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.resize_disk(node_data.drive_name, node_data.extend)
await node.resize_disk(node_data.drive_name, node_data.extend)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_qemu_node(project_id: UUID, node_id: UUID):
responses=responses)
async def start_qemu_node(node: QemuVM = Depends(dep_node)):
"""
Start a Qemu node.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
hardware_accel = qemu_manager.config.get_section_config("Qemu").getboolean("enable_hardware_acceleration", True)
if sys.platform.startswith("linux"):
# the enable_kvm option was used before version 2.0 and has priority
enable_kvm = qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm")
if enable_kvm is not None:
hardware_accel = enable_kvm
if hardware_accel and "-no-kvm" not in vm.options and "-no-hax" not in vm.options:
if hardware_accel and "-no-kvm" not in node.options and "-no-hax" not in node.options:
pm = ProjectManager.instance()
if pm.check_hardware_virtualization(vm) is False:
if pm.check_hardware_virtualization(node) is False:
pass #FIXME: check this
#raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
await vm.start()
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_qemu_node(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_qemu_node(node: QemuVM = Depends(dep_node)):
"""
Stop a Qemu node.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop()
await node.stop()
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reload_qemu_node(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_qemu_node(node: QemuVM = Depends(dep_node)):
"""
Reload a Qemu node.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reload()
await node.reload()
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_qemu_node(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_qemu_node(node: QemuVM = Depends(dep_node)):
"""
Suspend a Qemu node.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.suspend()
await node.suspend()
@router.post("/{node_id}/resume",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def resume_qemu_node(project_id: UUID, node_id: UUID):
responses=responses)
async def resume_qemu_node(node: QemuVM = Depends(dep_node)):
"""
Resume a Qemu node.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.resume()
await node.resume()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_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.
The port number on the Qemu node is always 0.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
nio = qemu_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await vm.adapter_add_nio_binding(adapter_number, nio)
nio = Qemu.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.adapter_add_nio_binding(adapter_number, nio)
return nio.__json__()
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def update_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.
The port number on the Qemu node is always 0.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number)
nio = node.get_nio(adapter_number)
if nio_data.filters:
nio.filters = nio_data.filters
if nio_data.suspend:
nio.suspend = nio_data.suspend
await vm.adapter_update_nio_binding(adapter_number, nio)
await node.adapter_update_nio_binding(adapter_number, nio)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
The port number on the Qemu node is always 0.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.adapter_remove_nio_binding(adapter_number)
await node.adapter_remove_nio_binding(adapter_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: QemuVM = Depends(dep_node)):
"""
Start a packet capture on the node.
The port number on the Qemu node is always 0.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
await vm.start_capture(adapter_number, pcap_file_path)
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(adapter_number, pcap_file_path)
return {"pcap_file_path": str(pcap_file_path)}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)):
"""
Stop a packet capture on the node.
The port number on the Qemu node is always 0.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop_capture(adapter_number)
await node.stop_capture(adapter_number)
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reset_console(project_id: UUID, node_id: UUID):
responses=responses)
async def reset_console(node: QemuVM = Depends(dep_node)):
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reset_console()
await node.reset_console()
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)):
"""
Stream the pcap capture file.
The port number on the Qemu node is always 0.
"""
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number)
stream = qemu_manager.stream_pcap_file(nio, vm.project.id)
nio = node.get_nio(adapter_number)
stream = Qemu.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -21,7 +21,7 @@ API endpoints for VirtualBox nodes.
import os
from fastapi import APIRouter, status
from fastapi import APIRouter, Depends, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
@ -30,9 +30,24 @@ from gns3server.endpoints import schemas
from gns3server.compute.virtualbox import VirtualBox
from gns3server.compute.virtualbox.virtualbox_error import VirtualBoxError
from gns3server.compute.project_manager import ProjectManager
from gns3server.compute.virtualbox.virtualbox_vm import VirtualBoxVM
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or VirtualBox node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
vbox_manager = VirtualBox.instance()
node = vbox_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.VirtualBox,
@ -69,250 +84,228 @@ async def create_virtualbox_node(project_id: UUID, node_data: schemas.VirtualBox
@router.get("/{node_id}",
response_model=schemas.VirtualBox,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_virtualbox_node(project_id: UUID, node_id: UUID):
responses=responses)
def get_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
"""
Return a VirtualBox node.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
return vm.__json__()
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.VirtualBox,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_virtualbox_node(project_id: UUID, node_id: UUID, node_data: schemas.VirtualBoxUpdate):
responses=responses)
async def update_virtualbox_node(node_data: schemas.VirtualBoxUpdate, node: VirtualBoxVM = Depends(dep_node)):
"""
Update a VirtualBox node.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
if "name" in node_data:
name = node_data.pop("name")
vmname = node_data.pop("vmname", None)
if name != vm.name:
oldname = vm.name
vm.name = name
if vm.linked_clone:
if name != node.name:
oldname = node.name
node.name = name
if node.linked_clone:
try:
await vm.set_vmname(vm.name)
await node.set_vmname(node.name)
except VirtualBoxError as e: # In case of error we rollback (we can't change the name when running)
vm.name = oldname
vm.updated()
node.name = oldname
node.updated()
raise e
if "adapters" in node_data:
adapters = node_data.pop("adapters")
if adapters != vm.adapters:
await vm.set_adapters(adapters)
if adapters != node.adapters:
await node.set_adapters(adapters)
if "ram" in node_data:
ram = node_data.pop("ram")
if ram != vm.ram:
await vm.set_ram(ram)
if ram != node.ram:
await node.set_ram(ram)
# update the console first to avoid issue if updating console type
vm.console = node_data.pop("console", vm.console)
node.console = node_data.pop("console", node.console)
for name, value in node_data.items():
if hasattr(vm, name) and getattr(vm, name) != value:
setattr(vm, name, value)
if hasattr(node, name) and getattr(node, name) != value:
setattr(node, name, value)
vm.updated()
return vm.__json__()
node.updated()
return node.__json__()
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_virtualbox_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
"""
Delete a VirtualBox node.
"""
# check the project_id exists
ProjectManager.instance().get_project(str(project_id))
await VirtualBox.instance().delete_node(str(node_id))
await VirtualBox.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_virtualbox_node(project_id: UUID, node_id: UUID):
responses=responses)
async def start_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
"""
Start a VirtualBox node.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
if await vm.check_hw_virtualization():
if await node.check_hw_virtualization():
pm = ProjectManager.instance()
if pm.check_hardware_virtualization(vm) is False:
pass # FIXME: check this
if pm.check_hardware_virtualization(node) is False:
pass # FIXME: check this
#raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
await vm.start()
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_virtualbox_node(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
"""
Stop a VirtualBox node.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop()
await node.stop()
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_virtualbox_node(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
"""
Suspend a VirtualBox node.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
await vm.suspend()
await node.suspend()
@router.post("/{node_id}/resume",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def resume_virtualbox_node(project_id: UUID, node_id: UUID):
responses=responses)
async def resume_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
"""
Resume a VirtualBox node.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
await vm.resume()
await node.resume()
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reload_virtualbox_node(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
"""
Reload a VirtualBox node.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reload()
await node.reload()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: VirtualBoxVM = Depends(dep_node)):
"""
Add a NIO (Network Input/Output) to the node.
The port number on the VirtualBox node is always 0.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
nio = vbox_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await vm.adapter_add_nio_binding(adapter_number, nio)
nio = VirtualBox.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.adapter_add_nio_binding(adapter_number, nio)
return nio.__json__()
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def update_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: VirtualBoxVM = Depends(dep_node)):
"""
Update a NIO (Network Input/Output) on the node.
The port number on the VirtualBox node is always 0.
"""
virtualbox_manager = VirtualBox.instance()
vm = virtualbox_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number)
nio = node.get_nio(adapter_number)
if nio_data.filters:
nio.filters = nio_data.filters
if nio_data.suspend:
nio.suspend = nio_data.suspend
await vm.adapter_update_nio_binding(adapter_number, nio)
await node.adapter_update_nio_binding(adapter_number, nio)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
The port number on the VirtualBox node is always 0.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
await vm.adapter_remove_nio_binding(adapter_number)
await node.adapter_remove_nio_binding(adapter_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: VirtualBoxVM = Depends(dep_node)):
"""
Start a packet capture on the node.
The port number on the VirtualBox node is always 0.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
await vm.start_capture(adapter_number, pcap_file_path)
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(adapter_number, pcap_file_path)
return {"pcap_file_path": str(pcap_file_path)}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
"""
Stop a packet capture on the node.
The port number on the VirtualBox node is always 0.
"""
vbox_manager = VirtualBox.instance()
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop_capture(adapter_number)
await node.stop_capture(adapter_number)
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reset_console(project_id: UUID, node_id: UUID):
responses=responses)
async def reset_console(node: VirtualBoxVM = Depends(dep_node)):
virtualbox_manager = VirtualBox.instance()
vm = virtualbox_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reset_console()
await node.reset_console()
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
"""
Stream the pcap capture file.
The port number on the VirtualBox node is always 0.
"""
virtualbox_manager = VirtualBox.instance()
vm = virtualbox_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number)
stream = virtualbox_manager.stream_pcap_file(nio, vm.project.id)
nio = node.get_nio(adapter_number)
stream = VirtualBox.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -21,7 +21,7 @@ API endpoints for VMware nodes.
import os
from fastapi import APIRouter, status
from fastapi import APIRouter, Depends, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
@ -29,9 +29,24 @@ from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.vmware import VMware
from gns3server.compute.project_manager import ProjectManager
from gns3server.compute.vmware.vmware_vm import VMwareVM
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or VMware node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
vmware_manager = VMware.instance()
node = vmware_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.VMware,
@ -62,238 +77,215 @@ async def create_vmware_node(project_id: UUID, node_data: schemas.VMwareCreate):
@router.get("/{node_id}",
response_model=schemas.VMware,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_vmware_node(project_id: UUID, node_id: UUID):
responses=responses)
def get_vmware_node(node: VMwareVM = Depends(dep_node)):
"""
Return a VMware node.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
return vm.__json__()
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.VMware,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def update_vmware_node(project_id: UUID, node_id: UUID, node_data: schemas.VMwareUpdate):
responses=responses)
def update_vmware_node(node_data: schemas.VMwareUpdate, node: VMwareVM = Depends(dep_node)):
"""
Update a VMware node.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
# update the console first to avoid issue if updating console type
vm.console = node_data.pop("console", vm.console)
node.console = node_data.pop("console", node.console)
for name, value in node_data.items():
if hasattr(vm, name) and getattr(vm, name) != value:
setattr(vm, name, value)
if hasattr(node, name) and getattr(node, name) != value:
setattr(node, name, value)
vm.updated()
return vm.__json__()
node.updated()
return node.__json__()
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_vmware_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_vmware_node(node: VMwareVM = Depends(dep_node)):
"""
Delete a VMware node.
"""
# check the project_id exists
ProjectManager.instance().get_project(str(project_id))
await VMware.instance().delete_node(str(node_id))
await VMware.instance().delete_node(node.id)
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_vmware_node(project_id: UUID, node_id: UUID):
responses=responses)
async def start_vmware_node(node: VMwareVM = Depends(dep_node)):
"""
Start a VMware node.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
if vm.check_hw_virtualization():
if node.check_hw_virtualization():
pm = ProjectManager.instance()
if pm.check_hardware_virtualization(vm) is False:
if pm.check_hardware_virtualization(node) is False:
pass # FIXME: check this
#raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
await vm.start()
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_vmware_node(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_vmware_node(node: VMwareVM = Depends(dep_node)):
"""
Stop a VMware node.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop()
await node.stop()
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_vmware_node(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_vmware_node(node: VMwareVM = Depends(dep_node)):
"""
Suspend a VMware node.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
await vm.suspend()
await node.suspend()
@router.post("/{node_id}/resume",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def resume_vmware_node(project_id: UUID, node_id: UUID):
responses=responses)
async def resume_vmware_node(node: VMwareVM = Depends(dep_node)):
"""
Resume a VMware node.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
await vm.resume()
await node.resume()
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reload_vmware_node(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_vmware_node(node: VMwareVM = Depends(dep_node)):
"""
Reload a VMware node.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reload()
await node.reload()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO,
node: VMwareVM = Depends(dep_node)):
"""
Add a NIO (Network Input/Output) to the node.
The port number on the VMware node is always 0.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
nio = vmware_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await vm.adapter_add_nio_binding(adapter_number, nio)
nio = VMware.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.adapter_add_nio_binding(adapter_number, nio)
return nio.__json__()
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def update_nio(adapter_number: int,
port_number: int,
nio_data: schemas.UDPNIO, node: VMwareVM = Depends(dep_node)):
"""
Update a NIO (Network Input/Output) on the node.
The port number on the VMware node is always 0.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number)
nio = node.get_nio(adapter_number)
if nio_data.filters:
nio.filters = nio_data.filters
await vm.adapter_update_nio_binding(adapter_number, nio)
await node.adapter_update_nio_binding(adapter_number, nio)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
The port number on the VMware node is always 0.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
await vm.adapter_remove_nio_binding(adapter_number)
await node.adapter_remove_nio_binding(adapter_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: VMwareVM = Depends(dep_node)):
"""
Start a packet capture on the node.
The port number on the VMware node is always 0.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
await vm.start_capture(adapter_number, pcap_file_path)
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(adapter_number, pcap_file_path)
return {"pcap_file_path": pcap_file_path}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
"""
Stop a packet capture on the node.
The port number on the VMware node is always 0.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop_capture(adapter_number)
await node.stop_capture(adapter_number)
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reset_console(project_id: UUID, node_id: UUID):
responses=responses)
async def reset_console(node: VMwareVM = Depends(dep_node)):
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reset_console()
await node.reset_console()
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
"""
Stream the pcap capture file.
The port number on the VMware node is always 0.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(adapter_number)
stream = vmware_manager.stream_pcap_file(nio, vm.project.id)
nio = node.get_nio(adapter_number)
stream = VMware.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
@router.post("/{node_id}/interfaces/vmnet",
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def allocate_vmnet(project_id: UUID, node_id: UUID) -> dict:
responses=responses)
def allocate_vmnet(node: VMwareVM = Depends(dep_node)) -> dict:
"""
Allocate a VMware VMnet interface on the server.
"""
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
vmware_manager.refresh_vmnet_list(ubridge=False)
vmnet = vmware_manager.allocate_vmnet()
vm.vmnets.append(vmnet)
node.vmnets.append(vmnet)
return {"vmnet": vmnet}

View File

@ -21,7 +21,7 @@ API endpoints for VPCS nodes.
import os
from fastapi import APIRouter, Body, status
from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from uuid import UUID
@ -29,10 +29,24 @@ from uuid import UUID
from gns3server.endpoints import schemas
from gns3server.compute.vpcs import VPCS
from gns3server.compute.project_manager import ProjectManager
from gns3server.compute.vpcs.vpcs_vm import VPCSVM
router = APIRouter()
responses = {
404: {"model": schemas.ErrorMessage, "description": "Could not find project or VMware node"}
}
def dep_node(project_id: UUID, node_id: UUID):
"""
Dependency to retrieve a node.
"""
vpcs_manager = VPCS.instance()
node = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
return node
@router.post("/",
response_model=schemas.VPCS,
@ -57,215 +71,190 @@ async def create_vpcs_node(project_id: UUID, node_data: schemas.VPCSCreate):
@router.get("/{node_id}",
response_model=schemas.VPCS,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def get_vpcs_node(project_id: UUID, node_id: UUID):
responses=responses)
def get_vpcs_node(node: VPCSVM = Depends(dep_node)):
"""
Return a VPCS node.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
return vm.__json__()
return node.__json__()
@router.put("/{node_id}",
response_model=schemas.VPCS,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
def update_vpcs_node(project_id: UUID, node_id: UUID, node_data: schemas.VPCSUpdate):
responses=responses)
def update_vpcs_node(node_data: schemas.VPCSUpdate, node: VPCSVM = Depends(dep_node)):
"""
Update a VPCS node.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
vm.name = node_data.get("name", vm.name)
vm.console = node_data.get("console", vm.console)
vm.console_type = node_data.get("console_type", vm.console_type)
vm.updated()
return vm.__json__()
node.name = node_data.get("name", node.name)
node.console = node_data.get("console", node.console)
node.console_type = node_data.get("console_type", node.console_type)
node.updated()
return node.__json__()
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_vpcs_node(project_id: UUID, node_id: UUID):
responses=responses)
async def delete_vpcs_node(node: VPCSVM = Depends(dep_node)):
"""
Delete a VPCS node.
"""
# check the project_id exists
ProjectManager.instance().get_project(str(project_id))
await VPCS.instance().delete_node(str(node_id))
await VPCS.instance().delete_node(node.id)
@router.post("/{node_id}/duplicate",
response_model=schemas.VPCS,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_vpcs_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
responses=responses)
async def duplicate_vpcs_node(destination_node_id: UUID = Body(..., embed=True), node: VPCSVM = Depends(dep_node)):
"""
Duplicate a VPCS node.
"""
new_node = await VPCS.instance().duplicate_node(str(node_id), str(destination_node_id))
new_node = await VPCS.instance().duplicate_node(node.id, str(destination_node_id))
return new_node.__json__()
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_vpcs_node(project_id: UUID, node_id: UUID):
responses=responses)
async def start_vpcs_node(node: VPCSVM = Depends(dep_node)):
"""
Start a VPCS node.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
await vm.start()
await node.start()
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_vpcs_node(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_vpcs_node(node: VPCSVM = Depends(dep_node)):
"""
Stop a VPCS node.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop()
await node.stop()
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def suspend_vpcs_node(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_vpcs_node(node: VPCSVM = Depends(dep_node)):
"""
Suspend a VPCS node.
Does nothing, suspend is not supported by VPCS.
"""
vpcs_manager = VPCS.instance()
vpcs_manager.get_node(str(node_id), project_id=str(project_id))
pass
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reload_vpcs_node(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_vpcs_node(node: VPCSVM = Depends(dep_node)):
"""
Reload a VPCS node.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reload()
await node.reload()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def create_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.
The adapter number on the VPCS node is always 0.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
nio = vpcs_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await vm.port_add_nio_binding(port_number, nio)
nio = VPCS.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
await node.port_add_nio_binding(port_number, nio)
return nio.__json__()
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
responses=responses)
async def update_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.
The adapter number on the VPCS node is always 0.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(port_number)
nio = node.get_nio(port_number)
if nio_data.filters:
nio.filters = nio_data.filters
await vm.port_update_nio_binding(port_number, nio)
await node.port_update_nio_binding(port_number, nio)
return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def delete_nio(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)):
"""
Delete a NIO (Network Input/Output) from the node.
The adapter number on the VPCS node is always 0.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
await vm.port_remove_nio_binding(port_number)
await node.port_remove_nio_binding(port_number)
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
responses=responses)
async def start_capture(adapter_number: int,
port_number: int,
node_capture_data: schemas.NodeCapture,
node: VPCSVM = Depends(dep_node)):
"""
Start a packet capture on the node.
The adapter number on the VPCS node is always 0.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
await vm.start_capture(adapter_number, pcap_file_path)
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
await node.start_capture(adapter_number, pcap_file_path)
return {"pcap_file_path": pcap_file_path}
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stop_capture(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)):
"""
Stop a packet capture on the node.
The adapter number on the VPCS node is always 0.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
await vm.stop_capture(port_number)
await node.stop_capture(port_number)
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def reset_console(project_id: UUID, node_id: UUID):
responses=responses)
async def reset_console(node: VPCSVM = Depends(dep_node)):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
await vm.reset_console()
await node.reset_console()
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
responses=responses)
async def stream_pcap_file(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)):
"""
Stream the pcap capture file.
The adapter number on the VPCS node is always 0.
"""
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
nio = vm.get_nio(port_number)
stream = vpcs_manager.stream_pcap_file(nio, vm.project.id)
nio = node.get_nio(port_number)
stream = VPCS.instance().stream_pcap_file(nio, node.project.id)
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")

View File

@ -33,12 +33,12 @@ router = APIRouter()
router.include_router(controller.router, tags=["controller"])
router.include_router(appliances.router, prefix="/appliances", tags=["appliances"])
router.include_router(computes.router, prefix="/computes", tags=["computes"])
router.include_router(drawings.router, tags=["drawings"])
router.include_router(drawings.router, prefix="/projects/{project_id}/drawings", tags=["drawings"])
router.include_router(gns3vm.router, prefix="/gns3vm", tags=["GNS3 VM"])
router.include_router(links.router, tags=["links"])
router.include_router(nodes.router, tags=["nodes"])
router.include_router(links.router, prefix="/projects/{project_id}/links", tags=["links"])
router.include_router(nodes.router, prefix="/projects/{project_id}/nodes", tags=["nodes"])
router.include_router(notifications.router, prefix="/notifications", tags=["notifications"])
router.include_router(projects.router, prefix="/projects", tags=["projects"])
router.include_router(snapshots.router, tags=["snapshots"])
router.include_router(snapshots.router, prefix="/projects/{project_id}/snapshots", tags=["snapshots"])
router.include_router(symbols.router, prefix="/symbols", tags=["symbols"])
router.include_router(templates.router, tags=["templates"])

View File

@ -25,8 +25,11 @@ from typing import Optional
router = APIRouter()
@router.get("/", summary="List of appliances")
async def list_appliances(update: Optional[bool] = None, symbol_theme: Optional[str] = "Classic"):
@router.get("/")
async def get_appliances(update: Optional[bool] = None, symbol_theme: Optional[str] = "Classic"):
"""
Return all appliances known by the controller.
"""
from gns3server.controller import Controller
controller = Controller.instance()

View File

@ -30,9 +30,12 @@ from gns3server.endpoints import schemas
router = APIRouter()
responses = {
404: {"model": ErrorMessage, "description": "Compute not found"}
}
@router.post("/",
summary="Create a new compute",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Compute,
responses={404: {"model": ErrorMessage, "description": "Could not connect to compute"},
@ -49,14 +52,12 @@ async def create_compute(compute_data: schemas.ComputeCreate):
@router.get("/{compute_id}",
summary="Get a compute",
response_model=schemas.Compute,
response_description="Compute data",
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Compute not found"}})
responses=responses)
def get_compute(compute_id: Union[str, UUID]):
"""
Get compute data from the controller.
Return a compute from the controller.
"""
compute = Controller.instance().get_compute(str(compute_id))
@ -64,13 +65,11 @@ def get_compute(compute_id: Union[str, UUID]):
@router.get("/",
summary="List of all computes",
response_model=List[schemas.Compute],
response_description="List of computes",
response_model_exclude_unset=True)
async def list_computes():
async def get_computes():
"""
Return the list of all computes known by the controller.
Return all computes known by the controller.
"""
controller = Controller.instance()
@ -78,11 +77,9 @@ async def list_computes():
@router.put("/{compute_id}",
summary="Update a compute",
response_model=schemas.Compute,
response_description="Updated compute",
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Compute not found"}})
responses=responses)
async def update_compute(compute_id: Union[str, UUID], compute_data: schemas.ComputeUpdate):
"""
Update a compute on the controller.
@ -95,9 +92,8 @@ async def update_compute(compute_id: Union[str, UUID], compute_data: schemas.Com
@router.delete("/{compute_id}",
summary="Delete a compute",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
responses=responses)
async def delete_compute(compute_id: Union[str, UUID]):
"""
Delete a compute from the controller.
@ -107,10 +103,8 @@ async def delete_compute(compute_id: Union[str, UUID]):
@router.get("/{compute_id}/{emulator}/images",
summary="List images",
response_description="List of images",
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
async def list_images(compute_id: Union[str, UUID], emulator: str):
responses=responses)
async def get_images(compute_id: Union[str, UUID], emulator: str):
"""
Return the list of images available on a compute for a given emulator type.
"""
@ -121,23 +115,24 @@ async def list_images(compute_id: Union[str, UUID], emulator: str):
@router.get("/{compute_id}/{emulator}/{endpoint_path:path}",
summary="Forward GET request to a compute",
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
responses=responses)
async def forward_get(compute_id: Union[str, UUID], emulator: str, endpoint_path: str):
"""
Forward GET request to a compute. Read the full compute API documentation for available endpoints.
Forward a GET request to a compute.
Read the full compute API documentation for available endpoints.
"""
compute = Controller.instance().get_compute(str(compute_id))
result = await compute.forward("GET", emulator, endpoint_path)
return result
@router.post("/{compute_id}/{emulator}/{endpoint_path:path}",
summary="Forward POST request to a compute",
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
responses=responses)
async def forward_post(compute_id: Union[str, UUID], emulator: str, endpoint_path: str, compute_data: dict):
"""
Forward POST request to a compute. Read the full compute API documentation for available endpoints.
Forward a POST request to a compute.
Read the full compute API documentation for available endpoints.
"""
compute = Controller.instance().get_compute(str(compute_id))
@ -145,11 +140,11 @@ async def forward_post(compute_id: Union[str, UUID], emulator: str, endpoint_pat
@router.put("/{compute_id}/{emulator}/{endpoint_path:path}",
summary="Forward PUT request to a compute",
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
responses=responses)
async def forward_put(compute_id: Union[str, UUID], emulator: str, endpoint_path: str, compute_data: dict):
"""
Forward PUT request to a compute. Read the full compute API documentation for available endpoints.
Forward a PUT request to a compute.
Read the full compute API documentation for available endpoints.
"""
compute = Controller.instance().get_compute(str(compute_id))
@ -157,11 +152,10 @@ async def forward_put(compute_id: Union[str, UUID], emulator: str, endpoint_path
@router.post("/{compute_id}/auto_idlepc",
summary="Find a new IDLE-PC value",
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
responses=responses)
async def autoidlepc(compute_id: Union[str, UUID], auto_idle_pc: schemas.AutoIdlePC):
"""
Find a suitable Idle-PC value for a given IOS image. This may take some time.
Find a suitable Idle-PC value for a given IOS image. This may take a few minutes.
"""
controller = Controller.instance()
@ -172,12 +166,11 @@ async def autoidlepc(compute_id: Union[str, UUID], auto_idle_pc: schemas.AutoIdl
@router.get("/{compute_id}/ports",
summary="Get ports used by a compute",
deprecated=True,
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
responses=responses)
async def ports(compute_id: Union[str, UUID]):
"""
Get ports information for a given compute.
Return ports information for a given compute.
"""
return await Controller.instance().compute_ports(str(compute_id))

View File

@ -37,7 +37,6 @@ router = APIRouter()
@router.post("/shutdown",
summary="Shutdown the local server",
status_code=status.HTTP_204_NO_CONTENT,
responses={403: {"model": ErrorMessage, "description": "Server shutdown not allowed"}})
async def shutdown():
@ -75,7 +74,7 @@ async def shutdown():
response_model=schemas.Version)
def version():
"""
Retrieve the server version number.
Return the server version number.
"""
config = Config.instance()
@ -106,7 +105,7 @@ def check_version(version: schemas.Version):
response_model=schemas.IOULicense)
def get_iou_license():
"""
Get the IOU license settings
Return the IOU license settings
"""
return Controller.instance().iou_license
@ -130,7 +129,7 @@ async def update_iou_license(iou_license: schemas.IOULicense):
@router.get("/statistics")
async def statistics():
"""
Retrieve server statistics.
Return server statistics.
"""
compute_statistics = []

View File

@ -30,13 +30,15 @@ from gns3server.endpoints.schemas.drawings import Drawing
router = APIRouter()
responses = {
404: {"model": ErrorMessage, "description": "Project or drawing not found"}
}
@router.get("/projects/{project_id}/drawings",
summary="List of all drawings",
@router.get("/",
response_model=List[Drawing],
response_description="List of drawings",
response_model_exclude_unset=True)
async def list_drawings(project_id: UUID):
async def get_drawings(project_id: UUID):
"""
Return the list of all drawings for a given project.
"""
@ -45,27 +47,27 @@ async def list_drawings(project_id: UUID):
return [v.__json__() for v in project.drawings.values()]
@router.post("/projects/{project_id}/drawings",
summary="Create a new drawing",
@router.post("/",
status_code=status.HTTP_201_CREATED,
response_model=Drawing,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
responses=responses)
async def create_drawing(project_id: UUID, drawing_data: Drawing):
"""
Create a new drawing.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
drawing = await project.add_drawing(**jsonable_encoder(drawing_data, exclude_unset=True))
return drawing.__json__()
@router.get("/projects/{project_id}/drawings/{drawing_id}",
summary="Get a drawing",
@router.get("/{drawing_id}",
response_model=Drawing,
response_description="Drawing data",
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Project or drawing not found"}})
responses=responses)
async def get_drawing(project_id: UUID, drawing_id: UUID):
"""
Get drawing data for a given project from the controller.
Return a drawing.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
@ -73,15 +75,13 @@ async def get_drawing(project_id: UUID, drawing_id: UUID):
return drawing.__json__()
@router.put("/projects/{project_id}/drawings/{drawing_id}",
summary="Update a drawing",
@router.put("/{drawing_id}",
response_model=Drawing,
response_description="Updated drawing",
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Project or drawing not found"}})
responses=responses)
async def update_drawing(project_id: UUID, drawing_id: UUID, drawing_data: Drawing):
"""
Update a drawing for a given project on the controller.
Update a drawing.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
@ -90,13 +90,12 @@ async def update_drawing(project_id: UUID, drawing_id: UUID, drawing_data: Drawi
return drawing.__json__()
@router.delete("/projects/{project_id}/drawings/{drawing_id}",
summary="Delete a drawing",
@router.delete("/{drawing_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Project or drawing not found"}})
responses=responses)
async def delete_drawing(project_id: UUID, drawing_id: UUID):
"""
Update a drawing for a given project from the controller.
Delete a drawing.
"""
project = await Controller.instance().get_loaded_project(str(project_id))

View File

@ -28,9 +28,8 @@ from gns3server.endpoints.schemas.gns3vm import GNS3VM
router = APIRouter()
@router.get("/engines",
summary="List of engines")
async def list_engines():
@router.get("/engines")
async def get_engines():
"""
Return the list of supported engines for the GNS3VM.
"""
@ -39,11 +38,10 @@ async def list_engines():
return gns3_vm.engine_list()
@router.get("/engines/{engine}/vms",
summary="List of VMs")
@router.get("/engines/{engine}/vms")
async def get_vms(engine: str):
"""
Get all the available VMs for a specific virtualization engine.
Return all the available VMs for a specific virtualization engine.
"""
vms = await Controller.instance().gns3vm.list(engine)
@ -51,19 +49,22 @@ async def get_vms(engine: str):
@router.get("/",
summary="Get GNS3 VM settings",
response_model=GNS3VM)
async def get_gns3vm_settings():
"""
Return the GNS3 VM settings.
"""
return Controller.instance().gns3vm.__json__()
@router.put("/",
summary="Update GNS3 VM settings",
response_model=GNS3VM,
response_description="Updated GNS3 VM settings",
response_model_exclude_unset=True)
async def update_gns3vm_settings(gns3vm_data: GNS3VM):
"""
Update the GNS3 VM settings.
"""
controller = Controller().instance()
gns3_vm = controller.gns3vm

View File

@ -19,7 +19,7 @@
API endpoints for links.
"""
from fastapi import APIRouter, Request, status
from fastapi import APIRouter, Depends, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from typing import List
@ -27,36 +27,47 @@ from uuid import UUID
from gns3server.controller import Controller
from gns3server.controller.controller_error import ControllerError
from gns3server.controller.link import Link
from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints.schemas.links import Link
import aiohttp
import multidict
from gns3server.endpoints import schemas
router = APIRouter()
responses = {
404: {"model": ErrorMessage, "description": "Could not find project or link"}
}
@router.get("/projects/{project_id}/links",
summary="List of all links",
response_model=List[Link],
response_description="List of links",
async def dep_link(project_id: UUID, link_id: UUID):
"""
Dependency to retrieve a link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
link = project.get_link(str(link_id))
return link
@router.get("/",
response_model=List[schemas.Link],
response_model_exclude_unset=True)
async def list_links(project_id: UUID):
async def get_links(project_id: UUID):
"""
Return all links for a given project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
return [v.__json__() for v in project.links.values()]
@router.post("/projects/{project_id}/links",
summary="Create a new link",
@router.post("/",
status_code=status.HTTP_201_CREATED,
response_model=Link,
response_model=schemas.Link,
responses={404: {"model": ErrorMessage, "description": "Could not find project"},
409: {"model": ErrorMessage, "description": "Could not create link"}})
async def create_link(project_id: UUID, link_data: Link):
async def create_link(project_id: UUID, link_data: schemas.Link):
"""
Create a new link on the controller.
Create a new link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
@ -78,42 +89,37 @@ async def create_link(project_id: UUID, link_data: Link):
return link.__json__()
@router.get("/projects/{project_id}/links/{link_id}/available_filters",
summary="List of filters",
responses={404: {"model": ErrorMessage, "description": "Could not find project or link"}})
async def list_filters(project_id: UUID, link_id: UUID):
@router.get("/{link_id}/available_filters",
responses=responses)
async def get_filters(link: Link = Depends(dep_link)):
"""
Return the list of filters available for this link.
Return all filters available for a given link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
link = project.get_link(str(link_id))
return link.available_filters()
@router.get("/projects/{project_id}/links/{link_id}",
summary="Get a link",
response_model=Link,
response_description="Link data",
@router.get("/{link_id}",
response_model=schemas.Link,
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Could not find project or link"}})
async def get_link(project_id: UUID, link_id: UUID):
responses=responses)
async def get_link(link: Link = Depends(dep_link)):
"""
Return a link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
link = project.get_link(str(link_id))
return link.__json__()
@router.put("/projects/{project_id}/links/{link_id}",
summary="Update a link",
response_model=Link,
response_description="Updated link",
@router.put("/{link_id}",
response_model=schemas.Link,
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
async def update_link(project_id: UUID, link_id: UUID, link_data: Link):
responses=responses)
async def update_link(link_data: schemas.Link, link: Link = Depends(dep_link)):
"""
Update a link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
link = project.get_link(str(link_id))
link_data = jsonable_encoder(link_data, exclude_unset=True)
if "filters" in link_data:
await link.update_filters(link_data["filters"])
@ -124,60 +130,53 @@ async def update_link(project_id: UUID, link_id: UUID, link_data: Link):
return link.__json__()
@router.post("/projects/{project_id}/links/{link_id}/start_capture",
summary="Start a packet capture",
@router.post("/{link_id}/start_capture",
status_code=status.HTTP_201_CREATED,
response_model=Link,
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
async def start_capture(project_id: UUID, link_id: UUID, capture_data: dict):
response_model=schemas.Link,
responses=responses)
async def start_capture(capture_data: dict, link: Link = Depends(dep_link)):
"""
Start packet capture on the link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
link = project.get_link(str(link_id))
await link.start_capture(data_link_type=capture_data.get("data_link_type", "DLT_EN10MB"),
capture_file_name=capture_data.get("capture_file_name"))
return link.__json__()
@router.post("/projects/{project_id}/links/{link_id}/stop_capture",
summary="Stop a packet capture",
@router.post("/{link_id}/stop_capture",
status_code=status.HTTP_201_CREATED,
response_model=Link,
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
async def stop_capture(project_id: UUID, link_id: UUID):
response_model=schemas.Link,
responses=responses)
async def stop_capture(link: Link = Depends(dep_link)):
"""
Stop packet capture on the link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
link = project.get_link(str(link_id))
await link.stop_capture()
return link.__json__()
@router.delete("/projects/{project_id}/links/{link_id}",
summary="Delete a link",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
async def delete(project_id: UUID, link_id: UUID):
@router.delete("/{link_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses=responses)
async def delete_link(project_id: UUID, link: Link = Depends(dep_link)):
"""
Delete link from the project.
Delete a link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
await project.delete_link(str(link_id))
await project.delete_link(link.id)
@router.post("/projects/{project_id}/links/{link_id}/reset",
summary="Reset a link",
response_model=Link,
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
async def reset(project_id: UUID, link_id: UUID):
@router.post("/{link_id}/reset",
response_model=schemas.Link,
responses=responses)
async def reset_link(link: Link = Depends(dep_link)):
"""
Reset a link.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
link = project.get_link(str(link_id))
await link.reset()
return link.__json__()

View File

@ -21,28 +21,28 @@ API endpoints for nodes.
import asyncio
from fastapi import APIRouter, Request, Response, status
from fastapi import APIRouter, Depends, Request, Response, status
from fastapi.encoders import jsonable_encoder
from fastapi.routing import APIRoute
from typing import List, Callable
from uuid import UUID
from gns3server.controller import Controller
from gns3server.controller.node import Node
from gns3server.controller.project import Project
from gns3server.utils import force_unix_path
from gns3server.controller.controller_error import ControllerForbiddenError
from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints import schemas
import aiohttp
node_locks = {}
class NodeConcurrency(APIRoute):
"""
To avoid strange effect we prevent concurrency
To avoid strange effects, we prevent concurrency
between the same instance of the node
(excepting when streaming a PCAP file and WebSocket consoles).
(excepting when streaming a PCAP file and for WebSocket consoles).
"""
def get_route_handler(self) -> Callable:
@ -74,25 +74,41 @@ class NodeConcurrency(APIRoute):
router = APIRouter(route_class=NodeConcurrency)
# # dependency to retrieve a node
# async def get_node(project_id: UUID, node_id: UUID):
#
# project = await Controller.instance().get_loaded_project(str(project_id))
# node = project.get_node(str(node_id))
# return node
responses = {
404: {"model": ErrorMessage, "description": "Could not find project or node"}
}
@router.post("/projects/{project_id}/nodes",
summary="Create a new node",
async def dep_project(project_id: UUID):
"""
Dependency to retrieve a project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
return project
async def dep_node(node_id: UUID, project: Project = Depends(dep_project)):
"""
Dependency to retrieve a node.
"""
node = project.get_node(str(node_id))
return node
@router.post("/",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Node,
responses={404: {"model": ErrorMessage, "description": "Could not find project"},
409: {"model": ErrorMessage, "description": "Could not create node"}})
async def create_node(project_id: UUID, node_data: schemas.Node):
async def create_node(node_data: schemas.Node, project: Project = Depends(dep_project)):
"""
Create a new node.
"""
controller = Controller.instance()
compute = controller.get_compute(str(node_data.compute_id))
project = await controller.get_loaded_project(str(project_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
node = await project.add_node(compute,
node_data.pop("name"),
@ -101,91 +117,82 @@ async def create_node(project_id: UUID, node_data: schemas.Node):
return node.__json__()
@router.get("/projects/{project_id}/nodes",
summary="List of all nodes",
@router.get("/",
response_model=List[schemas.Node],
response_description="List of nodes",
response_model_exclude_unset=True)
async def list_nodes(project_id: UUID):
async def get_nodes(project: Project = Depends(dep_project)):
"""
Return all nodes belonging to a given project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
return [v.__json__() for v in project.nodes.values()]
@router.post("/projects/{project_id}/nodes/start",
summary="Start all nodes",
@router.post("/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def start_all_nodes(project_id: UUID):
responses=responses)
async def start_all_nodes(project: Project = Depends(dep_project)):
"""
Start all nodes belonging to the project
Start all nodes belonging to a given project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
await project.start_all()
@router.post("/projects/{project_id}/nodes/stop",
summary="Stop all nodes",
@router.post("/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def stop_all_nodes(project_id: UUID):
responses=responses)
async def stop_all_nodes(project: Project = Depends(dep_project)):
"""
Stop all nodes belonging to the project
Stop all nodes belonging to a given project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
await project.stop_all()
@router.post("/projects/{project_id}/nodes/suspend",
summary="Stop all nodes",
@router.post("/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def suspend_all_nodes(project_id: UUID):
responses=responses)
async def suspend_all_nodes(project: Project = Depends(dep_project)):
"""
Suspend all nodes belonging to the project
Suspend all nodes belonging to a given project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
await project.suspend_all()
@router.post("/projects/{project_id}/nodes/reload",
summary="Reload all nodes",
@router.post("/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def reload_all_nodes(project_id: UUID):
responses=responses)
async def reload_all_nodes(project: Project = Depends(dep_project)):
"""
Reload all nodes belonging to the project
Reload all nodes belonging to a given project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
await project.stop_all()
await project.start_all()
@router.get("/projects/{project_id}/nodes/{node_id}",
summary="Get a node",
@router.get("/{node_id}",
response_model=schemas.Node,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
def get_node(project_id: UUID, node_id: UUID):
responses=responses)
def get_node(node: Node = Depends(dep_node)):
"""
Return a node from a given project.
"""
project = Controller.instance().get_project(str(project_id))
node = project.get_node(str(node_id))
return node.__json__()
@router.put("/projects/{project_id}/nodes/{node_id}",
summary="Update a node",
@router.put("/{node_id}",
response_model=schemas.Node,
response_description="Updated node",
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Project or node not found"}})
async def update_node(project_id: UUID, node_id: UUID, node_data: schemas.NodeUpdate):
responses=responses)
async def update_node(node_data: schemas.NodeUpdate, node: Node = Depends(dep_node)):
"""
Update a node.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
node_data = jsonable_encoder(node_data, exclude_unset=True)
# Ignore these because we only use them when creating a node
@ -197,139 +204,129 @@ async def update_node(project_id: UUID, node_id: UUID, node_data: schemas.NodeUp
return node.__json__()
@router.delete("/projects/{project_id}/nodes/{node_id}",
summary="Delete a node",
@router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"},
responses={**responses,
409: {"model": ErrorMessage, "description": "Cannot delete node"}})
async def delete_node(project_id: UUID, node_id: UUID):
async def delete_node(node_id: UUID, project: Project = Depends(dep_project)):
"""
Delete a node from a project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
await project.delete_node(str(node_id))
@router.post("/projects/{project_id}/nodes/{node_id}/duplicate",
summary="Duplicate a node",
@router.post("/{node_id}/duplicate",
response_model=schemas.Node,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def duplicate_node(project_id: UUID, node_id: UUID, duplicate_data: schemas.NodeDuplicate):
responses=responses)
async def duplicate_node(duplicate_data: schemas.NodeDuplicate, node: Node = Depends(dep_node)):
"""
Duplicate a node.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
new_node = await project.duplicate_node(node,
duplicate_data.x,
duplicate_data.y,
duplicate_data.z)
new_node = await node.project.duplicate_node(node,
duplicate_data.x,
duplicate_data.y,
duplicate_data.z)
return new_node.__json__()
@router.post("/projects/{project_id}/nodes/{node_id}/start",
summary="Start a node",
@router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def start_node(project_id: UUID, node_id: UUID, start_data: dict):
responses=responses)
async def start_node(start_data: dict, node: Node = Depends(dep_node)):
"""
Start a node.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
await node.start(data=start_data)
@router.post("/projects/{project_id}/nodes/{node_id}/stop",
summary="Stop a node",
@router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def stop_node(project_id: UUID, node_id: UUID):
responses=responses)
async def stop_node(node: Node = Depends(dep_node)):
"""
Stop a node.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
await node.stop()
@router.post("/projects/{project_id}/nodes/{node_id}/suspend",
summary="Suspend a node",
@router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def suspend_node(project_id: UUID, node_id: UUID):
responses=responses)
async def suspend_node(node: Node = Depends(dep_node)):
"""
Suspend a node.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
await node.suspend()
@router.post("/projects/{project_id}/nodes/{node_id}/reload",
summary="Reload a node",
@router.post("/{node_id}/reload",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def reload_node(project_id: UUID, node_id: UUID):
responses=responses)
async def reload_node(node: Node = Depends(dep_node)):
"""
Reload a node.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
await node.reload()
@router.get("/projects/{project_id}/nodes/{node_id}/links",
summary="List of all node links",
@router.get("/{node_id}/links",
response_model=List[schemas.Link],
response_description="List of links",
response_model_exclude_unset=True)
async def node_links(project_id: UUID, node_id: UUID):
async def get_node_links(node: Node = Depends(dep_node)):
"""
Return all the links connected to the node.
Return all the links connected to a node.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
links = []
for link in node.links:
links.append(link.__json__())
return links
@router.get("/projects/{project_id}/nodes/{node_id}/dynamips/auto_idlepc",
summary="Compute an Idle-PC",
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def auto_idlepc(project_id: UUID, node_id: UUID):
@router.get("/{node_id}/dynamips/auto_idlepc",
responses=responses)
async def auto_idlepc(node: Node = Depends(dep_node)):
"""
Compute an Idle-PC value for a Dynamips node
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
return await node.dynamips_auto_idlepc()
@router.get("/projects/{project_id}/nodes/{node_id}/dynamips/idlepc_proposals",
summary="Compute list of Idle-PC values",
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def idlepc_proposals(project_id: UUID, node_id: UUID):
@router.get("/{node_id}/dynamips/idlepc_proposals",
responses=responses)
async def idlepc_proposals(node: Node = Depends(dep_node)):
"""
Compute a list of potential idle-pc values for a Dynamips node
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
return await node.dynamips_idlepc_proposals()
@router.post("/projects/{project_id}/nodes/{node_id}/resize_disk",
summary="Resize a disk",
@router.post("/{node_id}/resize_disk",
status_code=status.HTTP_201_CREATED,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def resize_disk(project_id: UUID, node_id: UUID, resize_data: dict):
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
responses=responses)
async def resize_disk(resize_data: dict, node: Node = Depends(dep_node)):
"""
Resize a disk image.
"""
await node.post("/resize_disk", **resize_data)
@router.get("/projects/{project_id}/nodes/{node_id}/files/{file_path:path}",
summary="Get a file in the node directory",
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def get_file(project_id: UUID, node_id: UUID, file_path: str):
@router.get("/{node_id}/files/{file_path:path}",
responses=responses)
async def get_file(file_path: str, node: Node = Depends(dep_node)):
"""
Return a file in the node directory
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
path = force_unix_path(file_path)
# Raise error if user try to escape
@ -339,18 +336,20 @@ async def get_file(project_id: UUID, node_id: UUID, file_path: str):
node_type = node.node_type
path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
res = await node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True)
res = await node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=node.project.id, path=path),
timeout=None,
raw=True)
return Response(res.body, media_type="application/octet-stream")
@router.post("/projects/{project_id}/nodes/{node_id}/files/{file_path:path}",
summary="Write a file in the node directory",
@router.post("/{node_id}/files/{file_path:path}",
status_code=status.HTTP_201_CREATED,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def post_file(project_id: UUID, node_id: UUID, file_path: str, request: Request):
responses=responses)
async def post_file(file_path: str, request: Request, node: Node = Depends(dep_node)):
"""
Write a file in the node directory.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
path = force_unix_path(file_path)
# Raise error if user try to escape
@ -362,7 +361,10 @@ async def post_file(project_id: UUID, node_id: UUID, file_path: str, request: Re
data = await request.body() #FIXME: are we handling timeout or large files correctly?
await node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True)
await node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=node.project.id, path=path),
data=data,
timeout=None,
raw=True)
# @Route.get(
@ -420,23 +422,20 @@ async def post_file(project_id: UUID, node_id: UUID, file_path: str, request: Re
# return ws
@router.post("/projects/{project_id}/nodes/console/reset",
@router.post("/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def reset_console_all(project_id: UUID):
responses=responses)
async def reset_console_all(project: Project = Depends(dep_project)):
"""
Reset console for all nodes belonging to the project.
"""
project = await Controller.instance().get_loaded_project(str(project_id))
await project.reset_console_all()
@router.post("/projects/{project_id}/nodes/{node_id}/console/reset",
@router.post("/{node_id}/console/reset",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
async def console_reset(project_id: UUID, node_id: UUID):
responses=responses)
async def console_reset(node: Node = Depends(dep_node)):
project = await Controller.instance().get_loaded_project(str(project_id))
node = project.get_node(str(node_id))
await node.post("/console/reset")#, request.json)

View File

@ -19,28 +19,7 @@
API endpoints for projects.
"""
from fastapi import APIRouter, Request, Body, HTTPException, status, WebSocket, WebSocketDisconnect
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse, FileResponse
from websockets.exceptions import WebSocketException
from typing import List
from pathlib import Path
from uuid import UUID
from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints import schemas
from gns3server.controller import Controller
from gns3server.controller.controller_error import ControllerError, ControllerForbiddenError
from gns3server.controller.import_project import import_project as import_controller_project
from gns3server.controller.export_project import export_project as export_controller_project
from gns3server.utils.asyncio import aiozipstream
from gns3server.config import Config
router = APIRouter()
import os
import aiohttp
import asyncio
import tempfile
import zipfile
@ -50,17 +29,52 @@ import time
import logging
log = logging.getLogger()
from fastapi import APIRouter, Depends, Request, Body, HTTPException, status, WebSocket, WebSocketDisconnect
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse, FileResponse
from websockets.exceptions import WebSocketException
from typing import List
from uuid import UUID
from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints import schemas
from gns3server.controller import Controller
from gns3server.controller.project import Project
from gns3server.controller.controller_error import ControllerError, ControllerForbiddenError
from gns3server.controller.import_project import import_project as import_controller_project
from gns3server.controller.export_project import export_project as export_controller_project
from gns3server.utils.asyncio import aiozipstream
from gns3server.config import Config
router = APIRouter()
responses = {
404: {"model": ErrorMessage, "description": "Could not find project"}
}
def dep_project(project_id: UUID):
"""
Dependency to retrieve a project.
"""
project = Controller.instance().get_project(str(project_id))
return project
CHUNK_SIZE = 1024 * 8 # 8KB
@router.post("/",
summary="Create project",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Project,
response_model_exclude_unset=True,
responses={409: {"model": ErrorMessage, "description": "Could not create project"}})
async def create_project(project_data: schemas.ProjectCreate):
"""
Create a new project.
"""
controller = Controller.instance()
project = await controller.add_project(**jsonable_encoder(project_data, exclude_unset=True))
@ -69,92 +83,105 @@ async def create_project(project_data: schemas.ProjectCreate):
@router.get("/",
summary="List of all projects",
response_model=List[schemas.Project],
response_description="List of projects",
response_model_exclude_unset=True)
def list_projects():
def get_projects():
"""
Return all projects.
"""
controller = Controller.instance()
return [p.__json__() for p in controller.projects.values()]
@router.get("/{project_id}",
summary="Get a project",
response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
def get_project(project_id: UUID):
responses=responses)
def get_project(project: Project = Depends(dep_project)):
"""
Return a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
return project.__json__()
@router.put("/{project_id}",
summary="Update a project",
response_model=schemas.Project,
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def update_project(project_id: UUID, project_data: schemas.ProjectUpdate):
responses=responses)
async def update_project(project_data: schemas.ProjectUpdate, project: Project = Depends(dep_project)):
"""
Update a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
await project.update(**jsonable_encoder(project_data, exclude_unset=True))
return project.__json__()
@router.delete("/{project_id}",
summary="Delete a project",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def delete_project(project_id: UUID):
responses=responses)
async def delete_project(project: Project = Depends(dep_project)):
"""
Delete a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
await project.delete()
controller.remove_project(project)
@router.get("/{project_id}/stats",
summary="Get a project statistics",
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
def get_project_stats(project_id: UUID):
responses=responses)
def get_project_stats(project: Project = Depends(dep_project)):
"""
Return a project statistics.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
return project.stats()
@router.post("/{project_id}/close",
summary="Close a project",
status_code=status.HTTP_204_NO_CONTENT,
responses={409: {"model": ErrorMessage, "description": "Could not create project"}})
async def close_project(project_id: UUID):
responses={
**responses,
409: {"model": ErrorMessage, "description": "Could not close project"}
})
async def close_project(project: Project = Depends(dep_project)):
"""
Close a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
await project.close()
@router.post("/{project_id}/open",
summary="Open a project",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Project,
responses={409: {"model": ErrorMessage, "description": "Could not create project"}})
async def open_project(project_id: UUID):
responses={
**responses,
409: {"model": ErrorMessage, "description": "Could not open project"}
})
async def open_project(project: Project = Depends(dep_project)):
"""
Open a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
await project.open()
return project.__json__()
@router.post("/load",
summary="Open a project (local server only)",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Project,
responses={409: {"model": ErrorMessage, "description": "Could not load project"}})
responses={
**responses,
409: {"model": ErrorMessage, "description": "Could not load project"}
})
async def load_project(path: str = Body(..., embed=True)):
"""
Load a project (local server only).
"""
controller = Controller.instance()
config = Config.instance()
@ -224,9 +251,8 @@ async def notification_ws(project_id: UUID, websocket: WebSocket):
@router.get("/{project_id}/export",
summary="Export project",
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def export_project(project_id: UUID,
responses=responses)
async def export_project(project: Project = Depends(dep_project),
include_snapshots: bool = False,
include_images: bool = False,
reset_mac_addresses: bool = False,
@ -235,9 +261,6 @@ async def export_project(project_id: UUID,
Export a project as a portable archive.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
compression_query = compression.lower()
if compression_query == "zip":
compression = zipfile.ZIP_DEFLATED
@ -277,10 +300,9 @@ async def export_project(project_id: UUID,
@router.post("/{project_id}/import",
summary="Import a project",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
responses=responses)
async def import_project(project_id: UUID, request: Request):
"""
Import a project from a portable archive.
@ -319,15 +341,16 @@ async def import_project(project_id: UUID, request: Request):
@router.post("/{project_id}/duplicate",
summary="Duplicate a project",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project"},
409: {"model": ErrorMessage, "description": "Could not duplicate project"}})
async def duplicate(project_id: UUID, project_data: schemas.ProjectDuplicate):
controller = Controller.instance()
project = await controller.get_loaded_project(str(project_id))
responses={
**responses,
409: {"model": ErrorMessage, "description": "Could not duplicate project"}
})
async def duplicate(project_data: schemas.ProjectDuplicate, project: Project = Depends(dep_project)):
"""
Duplicate a project.
"""
if project_data.path:
config = Config.instance()
@ -343,13 +366,11 @@ async def duplicate(project_id: UUID, project_data: schemas.ProjectDuplicate):
@router.get("/{project_id}/files/{file_path:path}")
async def get_file(project_id: UUID, file_path: str):
async def get_file(file_path: str, project: Project = Depends(dep_project)):
"""
Get a file from a project.
Return a file from a project.
"""
controller = Controller.instance()
project = await controller.get_loaded_project(str(project_id))
path = os.path.normpath(file_path).strip('/')
# Raise error if user try to escape
@ -365,10 +386,11 @@ async def get_file(project_id: UUID, file_path: str):
@router.post("/{project_id}/files/{file_path:path}",
status_code=status.HTTP_204_NO_CONTENT)
async def write_file(project_id: UUID, file_path: str, request: Request):
async def write_file(file_path: str, request: Request, project: Project = Depends(dep_project)):
"""
Write a file from a project.
"""
controller = Controller.instance()
project = await controller.get_loaded_project(str(project_id))
path = os.path.normpath(file_path).strip("/")
# Raise error if user try to escape

View File

@ -19,75 +19,81 @@
API endpoints for snapshots.
"""
from fastapi import APIRouter, status
import logging
log = logging.getLogger()
from fastapi import APIRouter, Depends, status
from typing import List
from uuid import UUID
from gns3server.controller.project import Project
from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints import schemas
from gns3server.controller import Controller
router = APIRouter()
import logging
log = logging.getLogger()
responses = {
404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}
}
@router.post("/projects/{project_id}/snapshots",
def dep_project(project_id: UUID):
"""
Dependency to retrieve a project.
"""
project = Controller.instance().get_project(str(project_id))
return project
@router.post("/",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Snapshot,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
async def create_snapshot(project_id: UUID, snapshot_data: schemas.SnapshotCreate):
responses=responses)
async def create_snapshot(snapshot_data: schemas.SnapshotCreate, project: Project = Depends(dep_project)):
"""
Create a new snapshot of the project.
Create a new snapshot of a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
snapshot = await project.snapshot(snapshot_data.name)
return snapshot.__json__()
@router.get("/projects/{project_id}/snapshots",
@router.get("/",
response_model=List[schemas.Snapshot],
response_description="List of snapshots",
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
def list_snapshots(project_id: UUID):
responses=responses)
def get_snapshots(project: Project = Depends(dep_project)):
"""
Return a list of snapshots belonging to the project.
Return all snapshots belonging to a given project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
snapshots = [s for s in project.snapshots.values()]
return [s.__json__() for s in sorted(snapshots, key=lambda s: (s.created_at, s.name))]
@router.delete("/projects/{project_id}/snapshots/{snapshot_id}",
@router.delete("/{snapshot_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}})
async def delete_snapshot(project_id: UUID, snapshot_id: UUID):
responses=responses)
async def delete_snapshot(snapshot_id: UUID, project: Project = Depends(dep_project)):
"""
Delete a snapshot belonging to the project.
Delete a snapshot.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
await project.delete_snapshot(str(snapshot_id))
@router.post("/projects/{project_id}/snapshots/{snapshot_id}/restore",
@router.post("/{snapshot_id}/restore",
status_code=status.HTTP_201_CREATED,
response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}})
async def restore_snapshot(project_id: UUID, snapshot_id: UUID):
responses=responses)
async def restore_snapshot(snapshot_id: UUID, project: Project = Depends(dep_project)):
"""
Restore a snapshot from the project.
Restore a snapshot.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
snapshot = project.get_snapshot(str(snapshot_id))
project = await snapshot.restore()
return project.__json__()

View File

@ -1,6 +1,6 @@
#!/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
# it under the terms of the GNU General Public License as published by
@ -20,9 +20,8 @@ API endpoints for symbols.
"""
import os
import shutil
from fastapi import APIRouter, Request, status, File, UploadFile
from fastapi import APIRouter, Request, status
from fastapi.responses import FileResponse
from gns3server.controller import Controller
@ -37,7 +36,7 @@ router = APIRouter()
@router.get("/")
def list_symbols():
def get_symbols():
controller = Controller.instance()
return controller.symbols.list()
@ -46,6 +45,9 @@ def list_symbols():
@router.get("/{symbol_id:path}/raw",
responses={404: {"model": ErrorMessage, "description": "Could not find symbol"}})
async def get_symbol(symbol_id: str):
"""
Download a symbol file.
"""
controller = Controller.instance()
try:
@ -76,9 +78,9 @@ async def upload_symbol(symbol_id: str, request: Request):
@router.get("/default_symbols")
def list_default_symbols():
def get_default_symbols():
"""
Return list of default symbols.
Return all default symbols.
"""
controller = Controller.instance()

View File

@ -22,6 +22,9 @@ API endpoints for templates.
import hashlib
import json
import logging
log = logging.getLogger(__name__)
from fastapi import APIRouter, Request, Response, HTTPException, status
from fastapi.encoders import jsonable_encoder
from typing import Union, List
@ -33,8 +36,10 @@ from gns3server.controller import Controller
router = APIRouter()
import logging
log = logging.getLogger(__name__)
responses = {
404: {"model": ErrorMessage, "description": "Could not find template"}
}
#template_create_models = Union[schemas.VPCSTemplateCreate, schemas.CloudTemplateCreate, schemas.IOUTemplateCreate]
#template_update_models = Union[schemas.VPCSTemplateUpdate, schemas.CloudTemplateUpdate, schemas.IOUTemplateUpdate]
@ -45,6 +50,9 @@ log = logging.getLogger(__name__)
status_code=status.HTTP_201_CREATED,
response_model=schemas.Template)
def create_template(template_data: schemas.TemplateCreate):
"""
Create a new template.
"""
controller = Controller.instance()
template = controller.template_manager.add_template(jsonable_encoder(template_data, exclude_unset=True))
@ -54,11 +62,13 @@ def create_template(template_data: schemas.TemplateCreate):
@router.get("/templates/{template_id}",
summary="List of all nodes",
response_model=schemas.Template,
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Could not find template"}})
responses=responses)
def get_template(template_id: UUID, request: Request, response: Response):
"""
Return a template.
"""
request_etag = request.headers.get("If-None-Match", "")
controller = Controller.instance()
@ -75,8 +85,11 @@ def get_template(template_id: UUID, request: Request, response: Response):
@router.put("/templates/{template_id}",
response_model=schemas.Template,
response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Template not found"}})
responses=responses)
def update_template(template_id: UUID, template_data: schemas.TemplateUpdate):
"""
Update a template.
"""
controller = Controller.instance()
template = controller.template_manager.get_template(str(template_id))
@ -86,8 +99,11 @@ def update_template(template_id: UUID, template_data: schemas.TemplateUpdate):
@router.delete("/templates/{template_id}",
status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find template"}})
responses=responses)
def delete_template(template_id: UUID):
"""
Delete a template.
"""
controller = Controller.instance()
controller.template_manager.delete_template(str(template_id))
@ -95,9 +111,11 @@ def delete_template(template_id: UUID):
@router.get("/templates",
response_model=List[schemas.Template],
response_description="List of templates",
response_model_exclude_unset=True)
def list_templates():
def get_templates():
"""
Return all templates.
"""
controller = Controller.instance()
return [c.__json__() for c in controller.template_manager.templates.values()]
@ -106,8 +124,11 @@ def list_templates():
@router.post("/templates/{template_id}/duplicate",
response_model=schemas.Template,
status_code=status.HTTP_201_CREATED,
responses={404: {"model": ErrorMessage, "description": "Could not find template"}})
responses=responses)
async def duplicate_template(template_id: UUID):
"""
Duplicate a template.
"""
controller = Controller.instance()
template = controller.template_manager.duplicate_template(str(template_id))
@ -119,6 +140,9 @@ async def duplicate_template(template_id: UUID):
status_code=status.HTTP_201_CREATED,
responses={404: {"model": ErrorMessage, "description": "Could not find project or template"}})
async def create_node_from_template(project_id: UUID, template_id: UUID, template_usage: schemas.TemplateUsage):
"""
Create a new node from a template.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))