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

View File

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

View File

@ -124,13 +124,13 @@ def statistics() -> dict:
@router.get("/qemu/binaries") @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) return await Qemu.binary_list(archs)
@router.get("/qemu/img-binaries") @router.get("/qemu/img-binaries")
async def list_img_binaries(): async def get_img_binaries():
return await Qemu.img_binary_list() return await Qemu.img_binary_list()

View File

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

View File

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

View File

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

View File

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

View File

@ -21,16 +21,31 @@ API endpoints for Frame Relay switch nodes.
import os import os
from fastapi import APIRouter, Body, status from fastapi import APIRouter, Depends, Body, status
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from uuid import UUID from uuid import UUID
from gns3server.endpoints import schemas from gns3server.endpoints import schemas
from gns3server.compute.dynamips import Dynamips from gns3server.compute.dynamips import Dynamips
from gns3server.compute.dynamips.nodes.frame_relay_switch import FrameRelaySwitch
router = APIRouter() 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("/", @router.post("/",
response_model=schemas.FrameRelaySwitch, 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}", @router.get("/{node_id}",
response_model=schemas.FrameRelaySwitch, response_model=schemas.FrameRelaySwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
def get_frame_relay_switch(project_id: UUID, node_id: UUID): def get_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
""" """
Return a Frame Relay switch 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__() return node.__json__()
@router.post("/{node_id}/duplicate", @router.post("/{node_id}/duplicate",
response_model=schemas.FrameRelaySwitch, response_model=schemas.FrameRelaySwitch,
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def duplicate_frame_relay_switch(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)): async def duplicate_frame_relay_switch(destination_node_id: UUID = Body(..., embed=True),
node: FrameRelaySwitch = Depends(dep_node)):
""" """
Duplicate a Frame Relay switch 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__() return new_node.__json__()
@router.put("/{node_id}", @router.put("/{node_id}",
response_model=schemas.FrameRelaySwitch, response_model=schemas.FrameRelaySwitch,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def update_frame_relay_switch(project_id: UUID, node_id: UUID, node_data: schemas.FrameRelaySwitchUpdate): async def update_frame_relay_switch(node_data: schemas.FrameRelaySwitchUpdate,
node: FrameRelaySwitch = Depends(dep_node)):
""" """
Update an Frame Relay switch 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) node_data = jsonable_encoder(node_data, exclude_unset=True)
if "name" in node_data and node.name != node_data["name"]: if "name" in node_data and node.name != node_data["name"]:
await node.set_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}", @router.delete("/{node_id}",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def delete_frame_relay_switch_node(project_id: UUID, node_id: UUID): async def delete_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
""" """
Delete a Frame Relay switch node. Delete a Frame Relay switch node.
""" """
dynamips_manager = Dynamips.instance() await Dynamips.instance().delete_node(node.id)
await dynamips_manager.delete_node(str(node_id))
@router.post("/{node_id}/start", @router.post("/{node_id}/start",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
def start_frame_relay_switch(project_id: UUID, node_id: UUID): def start_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
""" """
Start a Frame Relay switch node. Start a Frame Relay switch node.
This endpoint results in no action since Frame Relay switch nodes are always on. 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", @router.post("/{node_id}/stop",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
def stop_frame_relay_switch(project_id: UUID, node_id: UUID): def stop_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
""" """
Stop a Frame Relay switch node. Stop a Frame Relay switch node.
This endpoint results in no action since Frame Relay switch nodes are always on. 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", @router.post("/{node_id}/suspend",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
def suspend_frame_relay_switch(project_id: UUID, node_id: UUID): def suspend_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
""" """
Suspend a Frame Relay switch node. Suspend a Frame Relay switch node.
This endpoint results in no action since Frame Relay switch nodes are always on. 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", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.UDPNIO, response_model=schemas.UDPNIO,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO): 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. Add a NIO (Network Input/Output) to the node.
The adapter number on the switch is always 0. The adapter number on the switch is always 0.
""" """
dynamips_manager = Dynamips.instance() nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
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))
await node.add_nio(nio, port_number) await node.add_nio(nio, port_number)
return nio.__json__() return nio.__json__()
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio", @router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int): async def delete_nio(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
""" """
Remove a NIO (Network Input/Output) from the node. Remove a NIO (Network Input/Output) from the node.
The adapter number on the switch is always 0. 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) nio = await node.remove_nio(port_number)
await nio.delete() await nio.delete()
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture): 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. Start a packet capture on the node.
The adapter number on the switch is always 0. 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) 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) await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
return {"pcap_file_path": pcap_file_path} 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", @router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int): async def stop_capture(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
""" """
Stop a packet capture on the node. Stop a packet capture on the node.
The adapter number on the switch is always 0. 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) await node.stop_capture(port_number)
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap", @router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}}) responses=responses)
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int): async def stream_pcap_file(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
""" """
Stream the pcap capture file. Stream the pcap capture file.
The adapter number on the hub is always 0. 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) 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") 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") @router.get("/qemu/images")
async def list_qemu_images() -> List[str]: async def get_qemu_images() -> List[str]:
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
return await qemu_manager.list_images() return await qemu_manager.list_images()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,8 +25,11 @@ from typing import Optional
router = APIRouter() router = APIRouter()
@router.get("/", summary="List of appliances") @router.get("/")
async def list_appliances(update: Optional[bool] = None, symbol_theme: Optional[str] = "Classic"): 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 from gns3server.controller import Controller
controller = Controller.instance() controller = Controller.instance()

View File

@ -30,9 +30,12 @@ from gns3server.endpoints import schemas
router = APIRouter() router = APIRouter()
responses = {
404: {"model": ErrorMessage, "description": "Compute not found"}
}
@router.post("/", @router.post("/",
summary="Create a new compute",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Compute, response_model=schemas.Compute,
responses={404: {"model": ErrorMessage, "description": "Could not connect to 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}", @router.get("/{compute_id}",
summary="Get a compute",
response_model=schemas.Compute, response_model=schemas.Compute,
response_description="Compute data",
response_model_exclude_unset=True, response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Compute not found"}}) responses=responses)
def get_compute(compute_id: Union[str, UUID]): 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)) compute = Controller.instance().get_compute(str(compute_id))
@ -64,13 +65,11 @@ def get_compute(compute_id: Union[str, UUID]):
@router.get("/", @router.get("/",
summary="List of all computes",
response_model=List[schemas.Compute], response_model=List[schemas.Compute],
response_description="List of computes",
response_model_exclude_unset=True) 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() controller = Controller.instance()
@ -78,11 +77,9 @@ async def list_computes():
@router.put("/{compute_id}", @router.put("/{compute_id}",
summary="Update a compute",
response_model=schemas.Compute, response_model=schemas.Compute,
response_description="Updated compute",
response_model_exclude_unset=True, 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): async def update_compute(compute_id: Union[str, UUID], compute_data: schemas.ComputeUpdate):
""" """
Update a compute on the controller. 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}", @router.delete("/{compute_id}",
summary="Delete a compute",
status_code=status.HTTP_204_NO_CONTENT, 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]): async def delete_compute(compute_id: Union[str, UUID]):
""" """
Delete a compute from the controller. 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", @router.get("/{compute_id}/{emulator}/images",
summary="List images", responses=responses)
response_description="List of images", async def get_images(compute_id: Union[str, UUID], emulator: str):
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
async def list_images(compute_id: Union[str, UUID], emulator: str):
""" """
Return the list of images available on a compute for a given emulator type. 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}", @router.get("/{compute_id}/{emulator}/{endpoint_path:path}",
summary="Forward GET request to a compute", responses=responses)
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
async def forward_get(compute_id: Union[str, UUID], emulator: str, endpoint_path: str): 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)) compute = Controller.instance().get_compute(str(compute_id))
result = await compute.forward("GET", emulator, endpoint_path) result = await compute.forward("GET", emulator, endpoint_path)
return result return result
@router.post("/{compute_id}/{emulator}/{endpoint_path:path}", @router.post("/{compute_id}/{emulator}/{endpoint_path:path}",
summary="Forward POST request to a compute", responses=responses)
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
async def forward_post(compute_id: Union[str, UUID], emulator: str, endpoint_path: str, compute_data: dict): 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)) 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}", @router.put("/{compute_id}/{emulator}/{endpoint_path:path}",
summary="Forward PUT request to a compute", responses=responses)
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
async def forward_put(compute_id: Union[str, UUID], emulator: str, endpoint_path: str, compute_data: dict): 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)) 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", @router.post("/{compute_id}/auto_idlepc",
summary="Find a new IDLE-PC value", responses=responses)
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
async def autoidlepc(compute_id: Union[str, UUID], auto_idle_pc: schemas.AutoIdlePC): 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() 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", @router.get("/{compute_id}/ports",
summary="Get ports used by a compute",
deprecated=True, deprecated=True,
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}}) responses=responses)
async def ports(compute_id: Union[str, UUID]): 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)) return await Controller.instance().compute_ports(str(compute_id))

View File

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

View File

@ -30,13 +30,15 @@ from gns3server.endpoints.schemas.drawings import Drawing
router = APIRouter() 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_model=List[Drawing],
response_description="List of drawings",
response_model_exclude_unset=True) 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. 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()] return [v.__json__() for v in project.drawings.values()]
@router.post("/projects/{project_id}/drawings", @router.post("/",
summary="Create a new drawing",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=Drawing, response_model=Drawing,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) responses=responses)
async def create_drawing(project_id: UUID, drawing_data: Drawing): async def create_drawing(project_id: UUID, drawing_data: Drawing):
"""
Create a new drawing.
"""
project = await Controller.instance().get_loaded_project(str(project_id)) project = await Controller.instance().get_loaded_project(str(project_id))
drawing = await project.add_drawing(**jsonable_encoder(drawing_data, exclude_unset=True)) drawing = await project.add_drawing(**jsonable_encoder(drawing_data, exclude_unset=True))
return drawing.__json__() return drawing.__json__()
@router.get("/projects/{project_id}/drawings/{drawing_id}", @router.get("/{drawing_id}",
summary="Get a drawing",
response_model=Drawing, response_model=Drawing,
response_description="Drawing data",
response_model_exclude_unset=True, 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): 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)) 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__() return drawing.__json__()
@router.put("/projects/{project_id}/drawings/{drawing_id}", @router.put("/{drawing_id}",
summary="Update a drawing",
response_model=Drawing, response_model=Drawing,
response_description="Updated drawing",
response_model_exclude_unset=True, 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): 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)) 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__() return drawing.__json__()
@router.delete("/projects/{project_id}/drawings/{drawing_id}", @router.delete("/{drawing_id}",
summary="Delete a drawing",
status_code=status.HTTP_204_NO_CONTENT, 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): 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)) 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 = APIRouter()
@router.get("/engines", @router.get("/engines")
summary="List of engines") async def get_engines():
async def list_engines():
""" """
Return the list of supported engines for the GNS3VM. Return the list of supported engines for the GNS3VM.
""" """
@ -39,11 +38,10 @@ async def list_engines():
return gns3_vm.engine_list() return gns3_vm.engine_list()
@router.get("/engines/{engine}/vms", @router.get("/engines/{engine}/vms")
summary="List of VMs")
async def get_vms(engine: str): 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) vms = await Controller.instance().gns3vm.list(engine)
@ -51,19 +49,22 @@ async def get_vms(engine: str):
@router.get("/", @router.get("/",
summary="Get GNS3 VM settings",
response_model=GNS3VM) response_model=GNS3VM)
async def get_gns3vm_settings(): async def get_gns3vm_settings():
"""
Return the GNS3 VM settings.
"""
return Controller.instance().gns3vm.__json__() return Controller.instance().gns3vm.__json__()
@router.put("/", @router.put("/",
summary="Update GNS3 VM settings",
response_model=GNS3VM, response_model=GNS3VM,
response_description="Updated GNS3 VM settings",
response_model_exclude_unset=True) response_model_exclude_unset=True)
async def update_gns3vm_settings(gns3vm_data: GNS3VM): async def update_gns3vm_settings(gns3vm_data: GNS3VM):
"""
Update the GNS3 VM settings.
"""
controller = Controller().instance() controller = Controller().instance()
gns3_vm = controller.gns3vm gns3_vm = controller.gns3vm

View File

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

View File

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

View File

@ -19,28 +19,7 @@
API endpoints for projects. 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 os
import aiohttp
import asyncio import asyncio
import tempfile import tempfile
import zipfile import zipfile
@ -50,17 +29,52 @@ import time
import logging import logging
log = logging.getLogger() 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 CHUNK_SIZE = 1024 * 8 # 8KB
@router.post("/", @router.post("/",
summary="Create project",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Project, response_model=schemas.Project,
response_model_exclude_unset=True, response_model_exclude_unset=True,
responses={409: {"model": ErrorMessage, "description": "Could not create project"}}) responses={409: {"model": ErrorMessage, "description": "Could not create project"}})
async def create_project(project_data: schemas.ProjectCreate): async def create_project(project_data: schemas.ProjectCreate):
"""
Create a new project.
"""
controller = Controller.instance() controller = Controller.instance()
project = await controller.add_project(**jsonable_encoder(project_data, exclude_unset=True)) 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("/", @router.get("/",
summary="List of all projects",
response_model=List[schemas.Project], response_model=List[schemas.Project],
response_description="List of projects",
response_model_exclude_unset=True) response_model_exclude_unset=True)
def list_projects(): def get_projects():
"""
Return all projects.
"""
controller = Controller.instance() controller = Controller.instance()
return [p.__json__() for p in controller.projects.values()] return [p.__json__() for p in controller.projects.values()]
@router.get("/{project_id}", @router.get("/{project_id}",
summary="Get a project",
response_model=schemas.Project, response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) responses=responses)
def get_project(project_id: UUID): def get_project(project: Project = Depends(dep_project)):
"""
Return a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
return project.__json__() return project.__json__()
@router.put("/{project_id}", @router.put("/{project_id}",
summary="Update a project",
response_model=schemas.Project, response_model=schemas.Project,
response_model_exclude_unset=True, response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) responses=responses)
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 a project.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
await project.update(**jsonable_encoder(project_data, exclude_unset=True)) await project.update(**jsonable_encoder(project_data, exclude_unset=True))
return project.__json__() return project.__json__()
@router.delete("/{project_id}", @router.delete("/{project_id}",
summary="Delete a project",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) responses=responses)
async def delete_project(project_id: UUID): async def delete_project(project: Project = Depends(dep_project)):
"""
Delete a project.
"""
controller = Controller.instance() controller = Controller.instance()
project = controller.get_project(str(project_id))
await project.delete() await project.delete()
controller.remove_project(project) controller.remove_project(project)
@router.get("/{project_id}/stats", @router.get("/{project_id}/stats",
summary="Get a project statistics", responses=responses)
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) def get_project_stats(project: Project = Depends(dep_project)):
def get_project_stats(project_id: UUID): """
Return a project statistics.
"""
controller = Controller.instance()
project = controller.get_project(str(project_id))
return project.stats() return project.stats()
@router.post("/{project_id}/close", @router.post("/{project_id}/close",
summary="Close a project",
status_code=status.HTTP_204_NO_CONTENT, status_code=status.HTTP_204_NO_CONTENT,
responses={409: {"model": ErrorMessage, "description": "Could not create project"}}) responses={
async def close_project(project_id: UUID): **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() await project.close()
@router.post("/{project_id}/open", @router.post("/{project_id}/open",
summary="Open a project",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Project, response_model=schemas.Project,
responses={409: {"model": ErrorMessage, "description": "Could not create project"}}) responses={
async def open_project(project_id: UUID): **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() await project.open()
return project.__json__() return project.__json__()
@router.post("/load", @router.post("/load",
summary="Open a project (local server only)",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Project, 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)): async def load_project(path: str = Body(..., embed=True)):
"""
Load a project (local server only).
"""
controller = Controller.instance() controller = Controller.instance()
config = Config.instance() config = Config.instance()
@ -224,9 +251,8 @@ async def notification_ws(project_id: UUID, websocket: WebSocket):
@router.get("/{project_id}/export", @router.get("/{project_id}/export",
summary="Export project", responses=responses)
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) async def export_project(project: Project = Depends(dep_project),
async def export_project(project_id: UUID,
include_snapshots: bool = False, include_snapshots: bool = False,
include_images: bool = False, include_images: bool = False,
reset_mac_addresses: 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. Export a project as a portable archive.
""" """
controller = Controller.instance()
project = controller.get_project(str(project_id))
compression_query = compression.lower() compression_query = compression.lower()
if compression_query == "zip": if compression_query == "zip":
compression = zipfile.ZIP_DEFLATED compression = zipfile.ZIP_DEFLATED
@ -277,10 +300,9 @@ async def export_project(project_id: UUID,
@router.post("/{project_id}/import", @router.post("/{project_id}/import",
summary="Import a project",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Project, response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) responses=responses)
async def import_project(project_id: UUID, request: Request): async def import_project(project_id: UUID, request: Request):
""" """
Import a project from a portable archive. 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", @router.post("/{project_id}/duplicate",
summary="Duplicate a project",
status_code=status.HTTP_201_CREATED, status_code=status.HTTP_201_CREATED,
response_model=schemas.Project, response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}, responses={
409: {"model": ErrorMessage, "description": "Could not duplicate project"}}) **responses,
async def duplicate(project_id: UUID, project_data: schemas.ProjectDuplicate): 409: {"model": ErrorMessage, "description": "Could not duplicate project"}
})
controller = Controller.instance() async def duplicate(project_data: schemas.ProjectDuplicate, project: Project = Depends(dep_project)):
project = await controller.get_loaded_project(str(project_id)) """
Duplicate a project.
"""
if project_data.path: if project_data.path:
config = Config.instance() 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}") @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('/') path = os.path.normpath(file_path).strip('/')
# Raise error if user try to escape # 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}", @router.post("/{project_id}/files/{file_path:path}",
status_code=status.HTTP_204_NO_CONTENT) 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("/") path = os.path.normpath(file_path).strip("/")
# Raise error if user try to escape # Raise error if user try to escape

View File

@ -19,75 +19,81 @@
API endpoints for snapshots. 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 typing import List
from uuid import UUID from uuid import UUID
from gns3server.controller.project import Project
from gns3server.endpoints.schemas.common import ErrorMessage from gns3server.endpoints.schemas.common import ErrorMessage
from gns3server.endpoints import schemas from gns3server.endpoints import schemas
from gns3server.controller import Controller from gns3server.controller import Controller
router = APIRouter() router = APIRouter()
import logging responses = {
log = logging.getLogger() 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, status_code=status.HTTP_201_CREATED,
response_model=schemas.Snapshot, response_model=schemas.Snapshot,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) responses=responses)
async def create_snapshot(project_id: UUID, snapshot_data: schemas.SnapshotCreate): 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) snapshot = await project.snapshot(snapshot_data.name)
return snapshot.__json__() return snapshot.__json__()
@router.get("/projects/{project_id}/snapshots", @router.get("/",
response_model=List[schemas.Snapshot], response_model=List[schemas.Snapshot],
response_description="List of snapshots",
response_model_exclude_unset=True, response_model_exclude_unset=True,
responses={404: {"model": ErrorMessage, "description": "Could not find project"}}) responses=responses)
def list_snapshots(project_id: UUID): 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()] snapshots = [s for s in project.snapshots.values()]
return [s.__json__() for s in sorted(snapshots, key=lambda s: (s.created_at, s.name))] 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, status_code=status.HTTP_204_NO_CONTENT,
responses={404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}}) responses=responses)
async def delete_snapshot(project_id: UUID, snapshot_id: UUID): 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)) 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, status_code=status.HTTP_201_CREATED,
response_model=schemas.Project, response_model=schemas.Project,
responses={404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}}) responses=responses)
async def restore_snapshot(project_id: UUID, snapshot_id: UUID): 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)) snapshot = project.get_snapshot(str(snapshot_id))
project = await snapshot.restore() project = await snapshot.restore()
return project.__json__() return project.__json__()

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2016 GNS3 Technologies Inc. # Copyright (C) 2020 GNS3 Technologies Inc.
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -20,9 +20,8 @@ API endpoints for symbols.
""" """
import os import os
import shutil
from fastapi import APIRouter, Request, status, File, UploadFile from fastapi import APIRouter, Request, status
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from gns3server.controller import Controller from gns3server.controller import Controller
@ -37,7 +36,7 @@ router = APIRouter()
@router.get("/") @router.get("/")
def list_symbols(): def get_symbols():
controller = Controller.instance() controller = Controller.instance()
return controller.symbols.list() return controller.symbols.list()
@ -46,6 +45,9 @@ def list_symbols():
@router.get("/{symbol_id:path}/raw", @router.get("/{symbol_id:path}/raw",
responses={404: {"model": ErrorMessage, "description": "Could not find symbol"}}) responses={404: {"model": ErrorMessage, "description": "Could not find symbol"}})
async def get_symbol(symbol_id: str): async def get_symbol(symbol_id: str):
"""
Download a symbol file.
"""
controller = Controller.instance() controller = Controller.instance()
try: try:
@ -76,9 +78,9 @@ async def upload_symbol(symbol_id: str, request: Request):
@router.get("/default_symbols") @router.get("/default_symbols")
def list_default_symbols(): def get_default_symbols():
""" """
Return list of default symbols. Return all default symbols.
""" """
controller = Controller.instance() controller = Controller.instance()

View File

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