mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-13 20:08:55 +00:00
Use dependencies and group common HTTP responses in endpoints.
This commit is contained in:
parent
58c5965905
commit
5341ccdbd6
@ -21,16 +21,31 @@ API endpoints for ATM switch nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.dynamips import Dynamips
|
||||
from gns3server.compute.dynamips.nodes.atm_switch import ATMSwitch
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or ATM switch node"}
|
||||
}
|
||||
|
||||
|
||||
async def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.ATMSwitch,
|
||||
@ -54,40 +69,36 @@ async def create_atm_switch(project_id: UUID, node_data: schemas.ATMSwitchCreate
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.ATMSwitch,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_atm_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_atm_switch(node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Return an ATM switch node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.ATMSwitch,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_atm_switch(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_atm_switch(destination_node_id: UUID = Body(..., embed=True), node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate an ATM switch node.
|
||||
"""
|
||||
|
||||
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.ATMSwitch,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_atm_switch(project_id: UUID, node_id: UUID, node_data: schemas.ATMSwitchUpdate):
|
||||
responses=responses)
|
||||
async def update_atm_switch(node_data: schemas.ATMSwitchUpdate, node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Update an ATM switch node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
if "name" in node_data and node.name != node_data["name"]:
|
||||
await node.set_name(node_data["name"])
|
||||
@ -99,94 +110,93 @@ async def update_atm_switch(project_id: UUID, node_id: UUID, node_data: schemas.
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_atm_switch_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_atm_switch_node(node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Delete an ATM switch node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
await dynamips_manager.delete_node(str(node_id))
|
||||
await Dynamips.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def start_atm_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def start_atm_switch(node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Start an ATM switch node.
|
||||
This endpoint results in no action since ATM switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def stop_atm_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def stop_atm_switch(node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stop an ATM switch node.
|
||||
This endpoint results in no action since ATM switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def suspend_atm_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def suspend_atm_switch(node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Suspend an ATM switch node.
|
||||
This endpoint results in no action since ATM switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.add_nio(nio, port_number)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Remove a NIO (Network Input/Output) from the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
@ -194,28 +204,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.stop_capture(port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: ATMSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
|
||||
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
@ -21,7 +21,7 @@ API endpoints for cloud nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, status
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from typing import Union
|
||||
@ -29,9 +29,24 @@ from uuid import UUID
|
||||
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.builtin import Builtin
|
||||
from gns3server.compute.builtin.nodes.cloud import Cloud
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or cloud node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.Cloud,
|
||||
@ -61,27 +76,23 @@ async def create_cloud(project_id: UUID, node_data: schemas.CloudCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.Cloud,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_cloud(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_cloud(node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Return a cloud node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.Cloud,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def update_cloud(project_id: UUID, node_id: UUID, node_data: schemas.CloudUpdate):
|
||||
responses=responses)
|
||||
def update_cloud(node_data: schemas.CloudUpdate, node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Update a cloud node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
for name, value in node_data.items():
|
||||
if hasattr(node, name) and getattr(node, name) != value:
|
||||
@ -92,69 +103,64 @@ def update_cloud(project_id: UUID, node_id: UUID, node_data: schemas.CloudUpdate
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_node(node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Delete a cloud node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
await builtin_manager.delete_node(str(node_id))
|
||||
await Builtin.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_cloud(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_cloud(node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Start a cloud node.
|
||||
"""
|
||||
|
||||
node = Builtin.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_cloud(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_cloud(node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Stop a cloud node.
|
||||
This endpoint results in no action since cloud nodes cannot be stopped.
|
||||
"""
|
||||
|
||||
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_cloud(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_cloud(node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a cloud node.
|
||||
This endpoint results in no action since cloud nodes cannot be suspended.
|
||||
"""
|
||||
|
||||
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID,
|
||||
node_id: UUID,
|
||||
adapter_number: int,
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO]):
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = builtin_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
nio = Builtin.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.add_nio(nio, port_number)
|
||||
return nio.__json__()
|
||||
|
||||
@ -162,19 +168,16 @@ async def create_nio(project_id: UUID,
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID,
|
||||
node_id: UUID,
|
||||
adapter_number: int,
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO]):
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
@ -184,28 +187,27 @@ async def update_nio(project_id: UUID,
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Remove a NIO (Network Input/Output) from the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.remove_nio(port_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
@ -213,28 +215,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.stop_capture(port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: Cloud = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
stream = builtin_manager.stream_pcap_file(nio, node.project.id)
|
||||
stream = Builtin.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
@ -124,13 +124,13 @@ def statistics() -> dict:
|
||||
|
||||
|
||||
@router.get("/qemu/binaries")
|
||||
async def list_binaries(archs: Optional[List[str]] = Body(None, embed=True)):
|
||||
async def get_binaries(archs: Optional[List[str]] = Body(None, embed=True)):
|
||||
|
||||
return await Qemu.binary_list(archs)
|
||||
|
||||
|
||||
@router.get("/qemu/img-binaries")
|
||||
async def list_img_binaries():
|
||||
async def get_img_binaries():
|
||||
|
||||
return await Qemu.img_binary_list()
|
||||
|
||||
|
@ -21,16 +21,31 @@ API endpoints for Docker nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.docker import Docker
|
||||
from gns3server.compute.docker.docker_vm import DockerVM
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Docker node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
node = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.Docker,
|
||||
@ -71,28 +86,23 @@ async def create_docker_node(project_id: UUID, node_data: schemas.DockerCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.Docker,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Return a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return container.__json__()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.Docker,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_docker(project_id: UUID, node_id: UUID, node_data: schemas.DockerUpdate):
|
||||
responses=responses)
|
||||
async def update_docker(node_data: schemas.DockerUpdate, node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
|
||||
props = [
|
||||
"name", "console", "console_type", "aux", "aux_type", "console_resolution",
|
||||
"console_http_port", "console_http_path", "start_command",
|
||||
@ -103,222 +113,201 @@ async def update_docker(project_id: UUID, node_id: UUID, node_data: schemas.Dock
|
||||
changed = False
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
for prop in props:
|
||||
if prop in node_data and node_data[prop] != getattr(container, prop):
|
||||
setattr(container, prop, node_data[prop])
|
||||
if prop in node_data and node_data[prop] != getattr(node, prop):
|
||||
setattr(node, prop, node_data[prop])
|
||||
changed = True
|
||||
# We don't call container.update for nothing because it will restart the container
|
||||
if changed:
|
||||
await container.update()
|
||||
container.updated()
|
||||
return container.__json__()
|
||||
await node.update()
|
||||
node.updated()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.start()
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.stop()
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.pause()
|
||||
await node.pause()
|
||||
|
||||
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Reload a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.restart()
|
||||
await node.restart()
|
||||
|
||||
|
||||
@router.post("/{node_id}/pause",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def pause_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def pause_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Pause a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.pause()
|
||||
await node.pause()
|
||||
|
||||
|
||||
@router.post("/{node_id}/unpause",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def unpause_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def unpause_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Unpause a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.unpause()
|
||||
await node.unpause()
|
||||
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_docker_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_docker_node(node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.delete()
|
||||
await node.delete()
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.Docker,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_docker_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_docker_node(destination_node_id: UUID = Body(..., embed=True), node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate a Docker node.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
new_node = await docker_manager.duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await Docker.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The port number on the Docker node is always 0.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = docker_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await container.adapter_add_nio_binding(adapter_number, nio)
|
||||
nio = Docker.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.adapter_add_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int,
|
||||
port_number: int, nio_data: schemas.UDPNIO,
|
||||
node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) on the node.
|
||||
The port number on the Docker node is always 0.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = container.get_nio(adapter_number)
|
||||
nio = node.get_nio(adapter_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
await container.adapter_update_nio_binding(adapter_number, nio)
|
||||
await node.adapter_update_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
The port number on the Docker node is always 0.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.adapter_remove_nio_binding(adapter_number)
|
||||
await node.adapter_remove_nio_binding(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The port number on the Docker node is always 0.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(container.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await container.start_capture(adapter_number, pcap_file_path)
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(adapter_number, pcap_file_path)
|
||||
return {"pcap_file_path": str(pcap_file_path)}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The port number on the Docker node is always 0.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.stop_capture(adapter_number)
|
||||
await node.stop_capture(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console(node: DockerVM = Depends(dep_node)):
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await container.reset_console()
|
||||
await node.reset_console()
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: DockerVM = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The port number on the Docker node is always 0.
|
||||
"""
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = container.get_nio(adapter_number)
|
||||
stream = docker_manager.stream_pcap_file(nio, container.project.id)
|
||||
nio = node.get_nio(adapter_number)
|
||||
stream = Docker.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
# @Route.get(
|
||||
|
@ -22,19 +22,23 @@ API endpoints for Dynamips nodes.
|
||||
import os
|
||||
import sys
|
||||
|
||||
from fastapi import APIRouter, status
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.compute.dynamips import Dynamips
|
||||
from gns3server.compute.dynamips.nodes.router import Router
|
||||
from gns3server.compute.dynamips.dynamips_error import DynamipsError
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
from gns3server.endpoints import schemas
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Dynamips node"}
|
||||
}
|
||||
|
||||
DEFAULT_CHASSIS = {
|
||||
"c1700": "1720",
|
||||
"c2600": "2610",
|
||||
@ -42,6 +46,16 @@ DEFAULT_CHASSIS = {
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.Dynamips,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
@ -74,169 +88,148 @@ async def create_router(project_id: UUID, node_data: schemas.DynamipsCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.Dynamips,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_router(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_router(node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Return Dynamips router.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return vm.__json__()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.Dynamips,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_router(project_id: UUID, node_id: UUID, node_data: schemas.DynamipsUpdate):
|
||||
responses=responses)
|
||||
async def update_router(node_data: schemas.DynamipsUpdate, node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Update a Dynamips router.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await dynamips_manager.update_vm_settings(vm, jsonable_encoder(node_data, exclude_unset=True))
|
||||
vm.updated()
|
||||
return vm.__json__()
|
||||
await Dynamips.instance().update_vm_settings(node, jsonable_encoder(node_data, exclude_unset=True))
|
||||
node.updated()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_router(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_router(node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Delete a Dynamips router.
|
||||
"""
|
||||
|
||||
# check the project_id exists
|
||||
ProjectManager.instance().get_project(str(project_id))
|
||||
await Dynamips.instance().delete_node(str(node_id))
|
||||
await Dynamips.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_router(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_router(node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Start a Dynamips router.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
try:
|
||||
await dynamips_manager.ghost_ios_support(vm)
|
||||
await Dynamips.instance().ghost_ios_support(node)
|
||||
except GeneratorExit:
|
||||
pass
|
||||
await vm.start()
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_router(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_router(node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Stop a Dynamips router.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop()
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_router(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_router(node: Router = Depends(dep_node)):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.suspend()
|
||||
await node.suspend()
|
||||
|
||||
|
||||
@router.post("/{node_id}/resume",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def resume_router(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def resume_router(node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Resume a suspended Dynamips router.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.resume()
|
||||
await node.resume()
|
||||
|
||||
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_router(node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Reload a suspended Dynamips router.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reload()
|
||||
await node.reload()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await dynamips_manager.create_nio(vm, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await vm.slot_add_nio_binding(adapter_number, port_number, nio)
|
||||
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.slot_add_nio_binding(adapter_number, port_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) on the node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number, port_number)
|
||||
nio = node.get_nio(adapter_number, port_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
await vm.slot_update_nio_binding(adapter_number, port_number, nio)
|
||||
await node.slot_update_nio_binding(adapter_number, port_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await vm.slot_remove_nio_binding(adapter_number, port_number)
|
||||
nio = await node.slot_remove_nio_binding(adapter_number, port_number)
|
||||
await nio.delete()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
# FIXME: Dynamips (Cygwin actually) doesn't like non ascii paths on Windows
|
||||
@ -245,72 +238,64 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
|
||||
except UnicodeEncodeError:
|
||||
raise DynamipsError('The capture file path "{}" must only contain ASCII (English) characters'.format(pcap_file_path))
|
||||
|
||||
await vm.start_capture(adapter_number, port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
await node.start_capture(adapter_number, port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop_capture(adapter_number, port_number)
|
||||
await node.stop_capture(adapter_number, port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number, port_number)
|
||||
stream = dynamips_manager.stream_pcap_file(nio, vm.project.id)
|
||||
nio = node.get_nio(adapter_number, port_number)
|
||||
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
||||
@router.get("/{node_id}/idlepc_proposals",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def get_idlepcs(project_id: UUID, node_id: UUID) -> List[str]:
|
||||
responses=responses)
|
||||
async def get_idlepcs(node: Router = Depends(dep_node)) -> List[str]:
|
||||
"""
|
||||
Retrieve Dynamips idle-pc proposals
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.set_idlepc("0x0")
|
||||
return await vm.get_idle_pc_prop()
|
||||
await node.set_idlepc("0x0")
|
||||
return await node.get_idle_pc_prop()
|
||||
|
||||
|
||||
@router.get("/{node_id}/auto_idlepc",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def get_auto_idlepc(project_id: UUID, node_id: UUID) -> dict:
|
||||
responses=responses)
|
||||
async def get_auto_idlepc(node: Router = Depends(dep_node)) -> dict:
|
||||
"""
|
||||
Get an automatically guessed best idle-pc value.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
idlepc = await dynamips_manager.auto_idlepc(vm)
|
||||
idlepc = await Dynamips.instance().auto_idlepc(node)
|
||||
return {"idlepc": idlepc}
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_router(project_id: UUID, node_id: UUID, destination_node_id: UUID):
|
||||
responses=responses)
|
||||
async def duplicate_router(destination_node_id: UUID, node: Router = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate a router.
|
||||
"""
|
||||
|
||||
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@ -330,9 +315,7 @@ async def duplicate_router(project_id: UUID, node_id: UUID, destination_node_id:
|
||||
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console(node: Router = Depends(dep_node)):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reset_console()
|
||||
await node.reset_console()
|
||||
|
@ -21,16 +21,31 @@ API endpoints for Ethernet hub nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.compute.dynamips import Dynamips
|
||||
from gns3server.compute.dynamips.nodes.ethernet_hub import EthernetHub
|
||||
from gns3server.endpoints import schemas
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Ethernet hub node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.EthernetHub,
|
||||
@ -54,40 +69,37 @@ async def create_ethernet_hub(project_id: UUID, node_data: schemas.EthernetHubCr
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.EthernetHub,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_ethernet_hub(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_ethernet_hub(node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Return an Ethernet hub.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.EthernetHub,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_ethernet_hub(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_ethernet_hub(destination_node_id: UUID = Body(..., embed=True),
|
||||
node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate an Ethernet hub.
|
||||
"""
|
||||
|
||||
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.EthernetHub,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_ethernet_hub(project_id: UUID, node_id: UUID, node_data: schemas.EthernetHubUpdate):
|
||||
responses=responses)
|
||||
async def update_ethernet_hub(node_data: schemas.EthernetHubUpdate, node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Update an Ethernet hub.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
if "name" in node_data and node.name != node_data["name"]:
|
||||
await node.set_name(node_data["name"])
|
||||
@ -99,94 +111,93 @@ async def update_ethernet_hub(project_id: UUID, node_id: UUID, node_data: schema
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_ethernet_hub(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_ethernet_hub(node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Delete an Ethernet hub.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
await dynamips_manager.delete_node(str(node_id))
|
||||
await Dynamips.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def start_ethernet_hub(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def start_ethernet_hub(node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Start an Ethernet hub.
|
||||
This endpoint results in no action since Ethernet hub nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def stop_ethernet_hub(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def stop_ethernet_hub(node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Stop an Ethernet hub.
|
||||
This endpoint results in no action since Ethernet hub nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def suspend_ethernet_hub(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def suspend_ethernet_hub(node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Suspend an Ethernet hub.
|
||||
This endpoint results in no action since Ethernet hub nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the hub is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.add_nio(nio, port_number)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
The adapter number on the hub is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The adapter number on the hub is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
@ -194,29 +205,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The adapter number on the hub is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.stop_capture(port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: EthernetHub = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The adapter number on the hub is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
|
||||
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
@ -21,21 +21,36 @@ API endpoints for Ethernet switch nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.compute.dynamips import Dynamips
|
||||
from gns3server.compute.dynamips.nodes.ethernet_switch import EthernetSwitch
|
||||
from gns3server.endpoints import schemas
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Ethernet switch node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.EthernetSwitch,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={409: {"model": schemas.ErrorMessage, "description": "Could not create Ethernet hub node"}})
|
||||
responses={409: {"model": schemas.ErrorMessage, "description": "Could not create Ethernet switch node"}})
|
||||
async def create_ethernet_switch(project_id: UUID, node_data: schemas.EthernetSwitchCreate):
|
||||
"""
|
||||
Create a new Ethernet switch.
|
||||
@ -57,37 +72,34 @@ async def create_ethernet_switch(project_id: UUID, node_data: schemas.EthernetSw
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.EthernetSwitch,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_ethernet_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.EthernetSwitch,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_ethernet_switch(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_ethernet_switch(destination_node_id: UUID = Body(..., embed=True),
|
||||
node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate an Ethernet switch.
|
||||
"""
|
||||
|
||||
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.EthernetSwitch,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_ethernet_switch(project_id: UUID, node_id: UUID, node_data: schemas.EthernetSwitchUpdate):
|
||||
responses=responses)
|
||||
async def update_ethernet_switch(node_data: schemas.EthernetSwitchUpdate, node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Update an Ethernet switch.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
if "name" in node_data and node.name != node_data["name"]:
|
||||
await node.set_name(node_data["name"])
|
||||
@ -102,119 +114,114 @@ async def update_ethernet_switch(project_id: UUID, node_id: UUID, node_data: sch
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_ethernet_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Delete an Ethernet switch.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
await dynamips_manager.delete_node(str(node_id))
|
||||
await Dynamips.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def start_ethernet_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def start_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Start an Ethernet switch.
|
||||
This endpoint results in no action since Ethernet switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def stop_ethernet_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def stop_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stop an Ethernet switch.
|
||||
This endpoint results in no action since Ethernet switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def suspend(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def suspend_ethernet_switch(node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Suspend an Ethernet switch.
|
||||
This endpoint results in no action since Ethernet switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: EthernetSwitch = Depends(dep_node)):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.add_nio(nio, port_number)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int,port_number: int, node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.stop_capture(port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: EthernetSwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
|
||||
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
@ -21,16 +21,31 @@ API endpoints for Frame Relay switch nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.dynamips import Dynamips
|
||||
from gns3server.compute.dynamips.nodes.frame_relay_switch import FrameRelaySwitch
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Frame Relay switch node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.FrameRelaySwitch,
|
||||
@ -54,40 +69,38 @@ async def create_frame_relay_switch(project_id: UUID, node_data: schemas.FrameRe
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.FrameRelaySwitch,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_frame_relay_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Return a Frame Relay switch node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.FrameRelaySwitch,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_frame_relay_switch(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_frame_relay_switch(destination_node_id: UUID = Body(..., embed=True),
|
||||
node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate a Frame Relay switch node.
|
||||
"""
|
||||
|
||||
new_node = await Dynamips.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await Dynamips.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.FrameRelaySwitch,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_frame_relay_switch(project_id: UUID, node_id: UUID, node_data: schemas.FrameRelaySwitchUpdate):
|
||||
responses=responses)
|
||||
async def update_frame_relay_switch(node_data: schemas.FrameRelaySwitchUpdate,
|
||||
node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Update an Frame Relay switch node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
if "name" in node_data and node.name != node_data["name"]:
|
||||
await node.set_name(node_data["name"])
|
||||
@ -99,94 +112,93 @@ async def update_frame_relay_switch(project_id: UUID, node_id: UUID, node_data:
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_frame_relay_switch_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Delete a Frame Relay switch node.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
await dynamips_manager.delete_node(str(node_id))
|
||||
await Dynamips.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def start_frame_relay_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def start_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Start a Frame Relay switch node.
|
||||
This endpoint results in no action since Frame Relay switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def stop_frame_relay_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def stop_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stop a Frame Relay switch node.
|
||||
This endpoint results in no action since Frame Relay switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def suspend_frame_relay_switch(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def suspend_frame_relay_switch(node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a Frame Relay switch node.
|
||||
This endpoint results in no action since Frame Relay switch nodes are always on.
|
||||
"""
|
||||
|
||||
Dynamips.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await dynamips_manager.create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
nio = await Dynamips.instance().create_nio(node, jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.add_nio(nio, port_number)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Remove a NIO (Network Input/Output) from the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
@ -194,28 +206,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The adapter number on the switch is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.stop_capture(port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: FrameRelaySwitch = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The adapter number on the hub is always 0.
|
||||
"""
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
stream = dynamips_manager.stream_pcap_file(nio, node.project.id)
|
||||
stream = Dynamips.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
@ -125,7 +125,7 @@ async def download_iou_image(filename: str):
|
||||
|
||||
|
||||
@router.get("/qemu/images")
|
||||
async def list_qemu_images() -> List[str]:
|
||||
async def get_qemu_images() -> List[str]:
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
return await qemu_manager.list_images()
|
||||
|
@ -21,7 +21,7 @@ API endpoints for IOU nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from typing import Union
|
||||
@ -29,9 +29,24 @@ from uuid import UUID
|
||||
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.iou import IOU
|
||||
from gns3server.compute.iou.iou_vm import IOUVM
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or IOU node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
node = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.IOU,
|
||||
@ -68,229 +83,204 @@ async def create_iou_node(project_id: UUID, node_data: schemas.IOUCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.IOU,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_iou_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_iou_node(node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Return an IOU node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return vm.__json__()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.IOU,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_iou_node(project_id: UUID, node_id: UUID, node_data: schemas.IOUUpdate):
|
||||
responses=responses)
|
||||
async def update_iou_node(node_data: schemas.IOUUpdate, node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Update an IOU node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
for name, value in node_data.items():
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
if hasattr(node, name) and getattr(node, name) != value:
|
||||
if name == "application_id":
|
||||
continue # we must ignore this to avoid overwriting the application_id allocated by the IOU manager
|
||||
setattr(vm, name, value)
|
||||
setattr(node, name, value)
|
||||
|
||||
if vm.use_default_iou_values:
|
||||
if node.use_default_iou_values:
|
||||
# update the default IOU values in case the image or use_default_iou_values have changed
|
||||
# this is important to have the correct NVRAM amount in order to correctly push the configs to the NVRAM
|
||||
await vm.update_default_iou_values()
|
||||
vm.updated()
|
||||
return vm.__json__()
|
||||
await node.update_default_iou_values()
|
||||
node.updated()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_iou_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_iou_node(node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete an IOU node.
|
||||
"""
|
||||
|
||||
await IOU.instance().delete_node(str(node_id))
|
||||
await IOU.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.IOU,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_iou_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_iou_node(destination_node_id: UUID = Body(..., embed=True), node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate an IOU node.
|
||||
"""
|
||||
|
||||
new_node = await IOU.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await IOU.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_iou_node(project_id: UUID, node_id: UUID, start_data: schemas.IOUStart):
|
||||
responses=responses)
|
||||
async def start_iou_node(start_data: schemas.IOUStart, node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Start an IOU node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
start_data = jsonable_encoder(start_data, exclude_unset=True)
|
||||
for name, value in start_data.items():
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
setattr(vm, name, value)
|
||||
if hasattr(node, name) and getattr(node, name) != value:
|
||||
setattr(node, name, value)
|
||||
|
||||
await vm.start()
|
||||
return vm.__json__()
|
||||
await node.start()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop(node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop an IOU node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop()
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def suspend_iou_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def suspend_iou_node(node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Suspend an IOU node.
|
||||
Does nothing since IOU doesn't support being suspended.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload_iou_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_iou_node(node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Reload an IOU node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reload()
|
||||
await node.reload()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID,
|
||||
node_id: UUID,
|
||||
adapter_number: int,
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO]):
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = iou_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await vm.adapter_add_nio_binding(adapter_number, port_number, nio)
|
||||
nio = IOU.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.adapter_add_nio_binding(adapter_number, port_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID,
|
||||
node_id: UUID,
|
||||
adapter_number: int,
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO]):
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) on the node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number, port_number)
|
||||
nio = node.get_nio(adapter_number, port_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
await vm.adapter_update_nio_binding(adapter_number, port_number, nio)
|
||||
return nio.__json__()
|
||||
await node.adapter_update_nio_binding(adapter_number, port_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.adapter_remove_nio_binding(adapter_number, port_number)
|
||||
await node.adapter_remove_nio_binding(adapter_number, port_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(adapter_number, pcap_file_path)
|
||||
return {"pcap_file_path": str(pcap_file_path)}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop_capture(adapter_number, port_number)
|
||||
await node.stop_capture(adapter_number, port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: IOUVM = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
"""
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number, port_number)
|
||||
stream = iou_manager.stream_pcap_file(nio, vm.project.id)
|
||||
nio = node.get_nio(adapter_number, port_number)
|
||||
stream = IOU.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console(node: IOUVM = Depends(dep_node)):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reset_console()
|
||||
await node.reset_console()
|
||||
|
||||
|
||||
# @Route.get(
|
||||
|
@ -21,7 +21,7 @@ API endpoints for NAT nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, status
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from typing import Union
|
||||
@ -29,9 +29,24 @@ from uuid import UUID
|
||||
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.builtin import Builtin
|
||||
from gns3server.compute.builtin.nodes.nat import Nat
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or NAT node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.NAT,
|
||||
@ -56,27 +71,23 @@ async def create_nat(project_id: UUID, node_data: schemas.NATCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.NAT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_nat(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_nat(node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Return a NAT node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.NAT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def update_nat(project_id: UUID, node_id: UUID, node_data: schemas.NATUpdate):
|
||||
responses=responses)
|
||||
def update_nat(node_data: schemas.NATUpdate, node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Update a NAT node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
for name, value in node_data.items():
|
||||
if hasattr(node, name) and getattr(node, name) != value:
|
||||
@ -87,69 +98,64 @@ def update_nat(project_id: UUID, node_id: UUID, node_data: schemas.NATUpdate):
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nat(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_nat(node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Delete a cloud node.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
await builtin_manager.delete_node(str(node_id))
|
||||
await Builtin.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_nat(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_nat(node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Start a NAT node.
|
||||
"""
|
||||
|
||||
node = Builtin.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_nat(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_nat(node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Stop a NAT node.
|
||||
This endpoint results in no action since cloud nodes cannot be stopped.
|
||||
"""
|
||||
|
||||
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_nat(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_nat(node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a NAT node.
|
||||
This endpoint results in no action since NAT nodes cannot be suspended.
|
||||
"""
|
||||
|
||||
Builtin.instance().get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID,
|
||||
node_id: UUID,
|
||||
adapter_number: int,
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO]):
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = builtin_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
nio = Builtin.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.add_nio(nio, port_number)
|
||||
return nio.__json__()
|
||||
|
||||
@ -157,19 +163,16 @@ async def create_nio(project_id: UUID,
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID,
|
||||
node_id: UUID,
|
||||
adapter_number: int,
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO]):
|
||||
nio_data: Union[schemas.EthernetNIO, schemas.TAPNIO, schemas.UDPNIO],
|
||||
node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
@ -179,28 +182,27 @@ async def update_nio(project_id: UUID,
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Remove a NIO (Network Input/Output) from the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.remove_nio(port_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(port_number, pcap_file_path, node_capture_data.data_link_type)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
@ -208,28 +210,24 @@ async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, po
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await node.stop_capture(port_number)
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: Nat = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The adapter number on the cloud is always 0.
|
||||
"""
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = node.get_nio(port_number)
|
||||
stream = builtin_manager.stream_pcap_file(nio, node.project.id)
|
||||
stream = Builtin.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
@ -20,30 +20,39 @@ API endpoints for projects.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import aiohttp
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Request, status
|
||||
import logging
|
||||
log = logging.getLogger()
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import FileResponse
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
from gns3server.compute.project import Project
|
||||
from gns3server.endpoints import schemas
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
import aiohttp
|
||||
import os
|
||||
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
|
||||
import logging
|
||||
log = logging.getLogger()
|
||||
|
||||
|
||||
# How many clients have subscribed to notifications
|
||||
_notifications_listening = {}
|
||||
|
||||
|
||||
def dep_project(project_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a project.
|
||||
"""
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
return project
|
||||
|
||||
|
||||
@router.get("/projects", response_model=List[schemas.Project])
|
||||
def get_projects():
|
||||
"""
|
||||
@ -73,41 +82,35 @@ def create_project(project_data: schemas.ProjectCreate):
|
||||
|
||||
@router.put("/projects/{project_id}",
|
||||
response_model=schemas.Project)
|
||||
async def update_project(project_id: UUID, project_data: schemas.ProjectUpdate):
|
||||
async def update_project(project_data: schemas.ProjectUpdate, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Update project on the compute.
|
||||
"""
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
await project.update(variables=project_data.variables)
|
||||
return project.__json__()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}",
|
||||
response_model=schemas.Project)
|
||||
def get_project(project_id: UUID):
|
||||
def get_project(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Return a project from the compute.
|
||||
"""
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
return project.__json__()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/close",
|
||||
status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def close_project(project_id: UUID):
|
||||
async def close_project(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Close a project on the compute.
|
||||
"""
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
if _notifications_listening.setdefault(project.id, 0) <= 1:
|
||||
await project.close()
|
||||
pm.remove_project(project.id)
|
||||
ProjectManager.instance().remove_project(project.id)
|
||||
try:
|
||||
del _notifications_listening[project.id]
|
||||
except KeyError:
|
||||
@ -118,15 +121,13 @@ async def close_project(project_id: UUID):
|
||||
|
||||
@router.delete("/projects/{project_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def delete_project(project_id: UUID):
|
||||
async def delete_project(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Delete project from the compute.
|
||||
"""
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
await project.delete()
|
||||
pm.remove_project(project.id)
|
||||
ProjectManager.instance().remove_project(project.id)
|
||||
|
||||
# @Route.get(
|
||||
# r"/projects/{project_id}/notifications",
|
||||
@ -184,24 +185,20 @@ async def delete_project(project_id: UUID):
|
||||
|
||||
@router.get("/projects/{project_id}/files",
|
||||
response_model=List[schemas.ProjectFile])
|
||||
async def get_project_files(project_id: UUID):
|
||||
async def get_project_files(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Return files belonging to a project.
|
||||
"""
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
return await project.list_files()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/files/{file_path:path}")
|
||||
async def get_file(project_id: UUID, file_path: str):
|
||||
async def get_file(file_path: str, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Get a file from a project.
|
||||
"""
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
path = os.path.normpath(file_path)
|
||||
|
||||
# Raise error if user try to escape
|
||||
@ -217,10 +214,8 @@ async def get_file(project_id: UUID, file_path: str):
|
||||
|
||||
@router.post("/projects/{project_id}/files/{file_path:path}",
|
||||
status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def write_file(project_id: UUID, file_path: str, request: Request):
|
||||
async def write_file(file_path: str, request: Request, project: Project = Depends(dep_project)):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(str(project_id))
|
||||
path = os.path.normpath(file_path)
|
||||
|
||||
# Raise error if user try to escape
|
||||
|
@ -22,7 +22,7 @@ API endpoints for Qemu nodes.
|
||||
import os
|
||||
import sys
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
@ -31,9 +31,24 @@ from gns3server.endpoints import schemas
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
from gns3server.compute.compute_error import ComputeError
|
||||
from gns3server.compute.qemu import Qemu
|
||||
from gns3server.compute.qemu.qemu_vm import QemuVM
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or Qemu node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
node = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.Qemu,
|
||||
@ -66,250 +81,224 @@ async def create_qemu_node(project_id: UUID, node_data: schemas.QemuCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.Qemu,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_qemu_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_qemu_node(node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Return a Qemu node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return vm.__json__()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.Qemu,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_qemu_node(project_id: UUID, node_id: UUID, node_data: schemas.QemuUpdate):
|
||||
responses=responses)
|
||||
async def update_qemu_node(node_data: schemas.QemuUpdate, node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a Qemu node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
# update the console first to avoid issue if updating console type
|
||||
vm.console = node_data.pop("console", vm.console)
|
||||
node.console = node_data.pop("console", node.console)
|
||||
for name, value in node_data.items():
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
await vm.update_property(name, value)
|
||||
vm.updated()
|
||||
return vm.__json__()
|
||||
if hasattr(node, name) and getattr(node, name) != value:
|
||||
await node.update_property(name, value)
|
||||
node.updated()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_qemu_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_qemu_node(node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a Qemu node.
|
||||
"""
|
||||
|
||||
await Qemu.instance().delete_node(str(node_id))
|
||||
await Qemu.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.Qemu,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_qemu_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_qemu_node(destination_node_id: UUID = Body(..., embed=True), node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate a Qemu node.
|
||||
"""
|
||||
|
||||
new_node = await Qemu.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await Qemu.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/resize_disk",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def resize_qemu_node_disk(project_id: UUID, node_id: UUID, node_data: schemas.QemuDiskResize):
|
||||
responses=responses)
|
||||
async def resize_qemu_node_disk(node_data: schemas.QemuDiskResize, node: QemuVM = Depends(dep_node)):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.resize_disk(node_data.drive_name, node_data.extend)
|
||||
await node.resize_disk(node_data.drive_name, node_data.extend)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_qemu_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_qemu_node(node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a Qemu node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
hardware_accel = qemu_manager.config.get_section_config("Qemu").getboolean("enable_hardware_acceleration", True)
|
||||
if sys.platform.startswith("linux"):
|
||||
# the enable_kvm option was used before version 2.0 and has priority
|
||||
enable_kvm = qemu_manager.config.get_section_config("Qemu").getboolean("enable_kvm")
|
||||
if enable_kvm is not None:
|
||||
hardware_accel = enable_kvm
|
||||
if hardware_accel and "-no-kvm" not in vm.options and "-no-hax" not in vm.options:
|
||||
if hardware_accel and "-no-kvm" not in node.options and "-no-hax" not in node.options:
|
||||
pm = ProjectManager.instance()
|
||||
if pm.check_hardware_virtualization(vm) is False:
|
||||
if pm.check_hardware_virtualization(node) is False:
|
||||
pass #FIXME: check this
|
||||
#raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
|
||||
await vm.start()
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_qemu_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_qemu_node(node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a Qemu node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop()
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload_qemu_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_qemu_node(node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Reload a Qemu node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reload()
|
||||
await node.reload()
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_qemu_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_qemu_node(node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a Qemu node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.suspend()
|
||||
await node.suspend()
|
||||
|
||||
|
||||
@router.post("/{node_id}/resume",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def resume_qemu_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def resume_qemu_node(node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Resume a Qemu node.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.resume()
|
||||
await node.resume()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The port number on the Qemu node is always 0.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = qemu_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await vm.adapter_add_nio_binding(adapter_number, nio)
|
||||
nio = Qemu.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.adapter_add_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) on the node.
|
||||
The port number on the Qemu node is always 0.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number)
|
||||
nio = node.get_nio(adapter_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
if nio_data.suspend:
|
||||
nio.suspend = nio_data.suspend
|
||||
await vm.adapter_update_nio_binding(adapter_number, nio)
|
||||
await node.adapter_update_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
The port number on the Qemu node is always 0.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.adapter_remove_nio_binding(adapter_number)
|
||||
await node.adapter_remove_nio_binding(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The port number on the Qemu node is always 0.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(adapter_number, pcap_file_path)
|
||||
return {"pcap_file_path": str(pcap_file_path)}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The port number on the Qemu node is always 0.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop_capture(adapter_number)
|
||||
await node.stop_capture(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console(node: QemuVM = Depends(dep_node)):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reset_console()
|
||||
await node.reset_console()
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: QemuVM = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The port number on the Qemu node is always 0.
|
||||
"""
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number)
|
||||
stream = qemu_manager.stream_pcap_file(nio, vm.project.id)
|
||||
nio = node.get_nio(adapter_number)
|
||||
stream = Qemu.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@ API endpoints for VirtualBox nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, status
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
@ -30,9 +30,24 @@ from gns3server.endpoints import schemas
|
||||
from gns3server.compute.virtualbox import VirtualBox
|
||||
from gns3server.compute.virtualbox.virtualbox_error import VirtualBoxError
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
from gns3server.compute.virtualbox.virtualbox_vm import VirtualBoxVM
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or VirtualBox node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
node = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.VirtualBox,
|
||||
@ -69,250 +84,228 @@ async def create_virtualbox_node(project_id: UUID, node_data: schemas.VirtualBox
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.VirtualBox,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_virtualbox_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Return a VirtualBox node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return vm.__json__()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.VirtualBox,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_virtualbox_node(project_id: UUID, node_id: UUID, node_data: schemas.VirtualBoxUpdate):
|
||||
responses=responses)
|
||||
async def update_virtualbox_node(node_data: schemas.VirtualBoxUpdate, node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a VirtualBox node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
|
||||
if "name" in node_data:
|
||||
name = node_data.pop("name")
|
||||
vmname = node_data.pop("vmname", None)
|
||||
if name != vm.name:
|
||||
oldname = vm.name
|
||||
vm.name = name
|
||||
if vm.linked_clone:
|
||||
if name != node.name:
|
||||
oldname = node.name
|
||||
node.name = name
|
||||
if node.linked_clone:
|
||||
try:
|
||||
await vm.set_vmname(vm.name)
|
||||
await node.set_vmname(node.name)
|
||||
except VirtualBoxError as e: # In case of error we rollback (we can't change the name when running)
|
||||
vm.name = oldname
|
||||
vm.updated()
|
||||
node.name = oldname
|
||||
node.updated()
|
||||
raise e
|
||||
|
||||
if "adapters" in node_data:
|
||||
adapters = node_data.pop("adapters")
|
||||
if adapters != vm.adapters:
|
||||
await vm.set_adapters(adapters)
|
||||
if adapters != node.adapters:
|
||||
await node.set_adapters(adapters)
|
||||
|
||||
if "ram" in node_data:
|
||||
ram = node_data.pop("ram")
|
||||
if ram != vm.ram:
|
||||
await vm.set_ram(ram)
|
||||
if ram != node.ram:
|
||||
await node.set_ram(ram)
|
||||
|
||||
# update the console first to avoid issue if updating console type
|
||||
vm.console = node_data.pop("console", vm.console)
|
||||
node.console = node_data.pop("console", node.console)
|
||||
|
||||
for name, value in node_data.items():
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
setattr(vm, name, value)
|
||||
if hasattr(node, name) and getattr(node, name) != value:
|
||||
setattr(node, name, value)
|
||||
|
||||
vm.updated()
|
||||
return vm.__json__()
|
||||
node.updated()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_virtualbox_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a VirtualBox node.
|
||||
"""
|
||||
|
||||
# check the project_id exists
|
||||
ProjectManager.instance().get_project(str(project_id))
|
||||
await VirtualBox.instance().delete_node(str(node_id))
|
||||
await VirtualBox.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_virtualbox_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a VirtualBox node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
if await vm.check_hw_virtualization():
|
||||
if await node.check_hw_virtualization():
|
||||
pm = ProjectManager.instance()
|
||||
if pm.check_hardware_virtualization(vm) is False:
|
||||
pass # FIXME: check this
|
||||
if pm.check_hardware_virtualization(node) is False:
|
||||
pass # FIXME: check this
|
||||
#raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
|
||||
await vm.start()
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_virtualbox_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a VirtualBox node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop()
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_virtualbox_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a VirtualBox node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.suspend()
|
||||
await node.suspend()
|
||||
|
||||
|
||||
@router.post("/{node_id}/resume",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def resume_virtualbox_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def resume_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Resume a VirtualBox node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.resume()
|
||||
await node.resume()
|
||||
|
||||
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload_virtualbox_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_virtualbox_node(node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Reload a VirtualBox node.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reload()
|
||||
await node.reload()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The port number on the VirtualBox node is always 0.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vbox_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await vm.adapter_add_nio_binding(adapter_number, nio)
|
||||
nio = VirtualBox.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.adapter_add_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) on the node.
|
||||
The port number on the VirtualBox node is always 0.
|
||||
"""
|
||||
|
||||
virtualbox_manager = VirtualBox.instance()
|
||||
vm = virtualbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number)
|
||||
nio = node.get_nio(adapter_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
if nio_data.suspend:
|
||||
nio.suspend = nio_data.suspend
|
||||
await vm.adapter_update_nio_binding(adapter_number, nio)
|
||||
await node.adapter_update_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
The port number on the VirtualBox node is always 0.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.adapter_remove_nio_binding(adapter_number)
|
||||
await node.adapter_remove_nio_binding(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The port number on the VirtualBox node is always 0.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(adapter_number, pcap_file_path)
|
||||
return {"pcap_file_path": str(pcap_file_path)}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The port number on the VirtualBox node is always 0.
|
||||
"""
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop_capture(adapter_number)
|
||||
await node.stop_capture(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console(node: VirtualBoxVM = Depends(dep_node)):
|
||||
|
||||
virtualbox_manager = VirtualBox.instance()
|
||||
vm = virtualbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reset_console()
|
||||
await node.reset_console()
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: VirtualBoxVM = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The port number on the VirtualBox node is always 0.
|
||||
"""
|
||||
|
||||
virtualbox_manager = VirtualBox.instance()
|
||||
vm = virtualbox_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number)
|
||||
stream = virtualbox_manager.stream_pcap_file(nio, vm.project.id)
|
||||
nio = node.get_nio(adapter_number)
|
||||
stream = VirtualBox.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@ API endpoints for VMware nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, status
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
@ -29,9 +29,24 @@ from uuid import UUID
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.vmware import VMware
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
from gns3server.compute.vmware.vmware_vm import VMwareVM
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or VMware node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
node = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.VMware,
|
||||
@ -62,238 +77,215 @@ async def create_vmware_node(project_id: UUID, node_data: schemas.VMwareCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.VMware,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_vmware_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_vmware_node(node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Return a VMware node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return vm.__json__()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.VMware,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def update_vmware_node(project_id: UUID, node_id: UUID, node_data: schemas.VMwareUpdate):
|
||||
responses=responses)
|
||||
def update_vmware_node(node_data: schemas.VMwareUpdate, node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a VMware node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
# update the console first to avoid issue if updating console type
|
||||
vm.console = node_data.pop("console", vm.console)
|
||||
node.console = node_data.pop("console", node.console)
|
||||
for name, value in node_data.items():
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
setattr(vm, name, value)
|
||||
if hasattr(node, name) and getattr(node, name) != value:
|
||||
setattr(node, name, value)
|
||||
|
||||
vm.updated()
|
||||
return vm.__json__()
|
||||
node.updated()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_vmware_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_vmware_node(node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a VMware node.
|
||||
"""
|
||||
|
||||
# check the project_id exists
|
||||
ProjectManager.instance().get_project(str(project_id))
|
||||
await VMware.instance().delete_node(str(node_id))
|
||||
await VMware.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_vmware_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_vmware_node(node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a VMware node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
if vm.check_hw_virtualization():
|
||||
if node.check_hw_virtualization():
|
||||
pm = ProjectManager.instance()
|
||||
if pm.check_hardware_virtualization(vm) is False:
|
||||
if pm.check_hardware_virtualization(node) is False:
|
||||
pass # FIXME: check this
|
||||
#raise ComputeError("Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
|
||||
await vm.start()
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_vmware_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_vmware_node(node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a VMware node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop()
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_vmware_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_vmware_node(node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a VMware node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.suspend()
|
||||
await node.suspend()
|
||||
|
||||
|
||||
@router.post("/{node_id}/resume",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def resume_vmware_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def resume_vmware_node(node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Resume a VMware node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.resume()
|
||||
await node.resume()
|
||||
|
||||
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload_vmware_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_vmware_node(node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Reload a VMware node.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reload()
|
||||
await node.reload()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO,
|
||||
node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The port number on the VMware node is always 0.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vmware_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await vm.adapter_add_nio_binding(adapter_number, nio)
|
||||
nio = VMware.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.adapter_add_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int,
|
||||
port_number: int,
|
||||
nio_data: schemas.UDPNIO, node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) on the node.
|
||||
The port number on the VMware node is always 0.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number)
|
||||
nio = node.get_nio(adapter_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
await vm.adapter_update_nio_binding(adapter_number, nio)
|
||||
await node.adapter_update_nio_binding(adapter_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
The port number on the VMware node is always 0.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.adapter_remove_nio_binding(adapter_number)
|
||||
await node.adapter_remove_nio_binding(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The port number on the VMware node is always 0.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(adapter_number, pcap_file_path)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The port number on the VMware node is always 0.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop_capture(adapter_number)
|
||||
await node.stop_capture(adapter_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console(node: VMwareVM = Depends(dep_node)):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reset_console()
|
||||
await node.reset_console()
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: VMwareVM = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The port number on the VMware node is always 0.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(adapter_number)
|
||||
stream = vmware_manager.stream_pcap_file(nio, vm.project.id)
|
||||
nio = node.get_nio(adapter_number)
|
||||
stream = VMware.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
||||
@router.post("/{node_id}/interfaces/vmnet",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def allocate_vmnet(project_id: UUID, node_id: UUID) -> dict:
|
||||
responses=responses)
|
||||
def allocate_vmnet(node: VMwareVM = Depends(dep_node)) -> dict:
|
||||
"""
|
||||
Allocate a VMware VMnet interface on the server.
|
||||
"""
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
vmware_manager.refresh_vmnet_list(ubridge=False)
|
||||
vmnet = vmware_manager.allocate_vmnet()
|
||||
vm.vmnets.append(vmnet)
|
||||
node.vmnets.append(vmnet)
|
||||
return {"vmnet": vmnet}
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@ API endpoints for VPCS nodes.
|
||||
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Body, status
|
||||
from fastapi import APIRouter, Depends, Body, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from uuid import UUID
|
||||
@ -29,10 +29,24 @@ from uuid import UUID
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.compute.vpcs import VPCS
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
|
||||
from gns3server.compute.vpcs.vpcs_vm import VPCSVM
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": schemas.ErrorMessage, "description": "Could not find project or VMware node"}
|
||||
}
|
||||
|
||||
|
||||
def dep_node(project_id: UUID, node_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
node = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
response_model=schemas.VPCS,
|
||||
@ -57,215 +71,190 @@ async def create_vpcs_node(project_id: UUID, node_data: schemas.VPCSCreate):
|
||||
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.VPCS,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_vpcs_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_vpcs_node(node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Return a VPCS node.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
return vm.__json__()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.VPCS,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
def update_vpcs_node(project_id: UUID, node_id: UUID, node_data: schemas.VPCSUpdate):
|
||||
responses=responses)
|
||||
def update_vpcs_node(node_data: schemas.VPCSUpdate, node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a VPCS node.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
vm.name = node_data.get("name", vm.name)
|
||||
vm.console = node_data.get("console", vm.console)
|
||||
vm.console_type = node_data.get("console_type", vm.console_type)
|
||||
vm.updated()
|
||||
return vm.__json__()
|
||||
node.name = node_data.get("name", node.name)
|
||||
node.console = node_data.get("console", node.console)
|
||||
node.console_type = node_data.get("console_type", node.console_type)
|
||||
node.updated()
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_vpcs_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_vpcs_node(node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a VPCS node.
|
||||
"""
|
||||
|
||||
# check the project_id exists
|
||||
ProjectManager.instance().get_project(str(project_id))
|
||||
await VPCS.instance().delete_node(str(node_id))
|
||||
await VPCS.instance().delete_node(node.id)
|
||||
|
||||
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.VPCS,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_vpcs_node(project_id: UUID, node_id: UUID, destination_node_id: UUID = Body(..., embed=True)):
|
||||
responses=responses)
|
||||
async def duplicate_vpcs_node(destination_node_id: UUID = Body(..., embed=True), node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate a VPCS node.
|
||||
"""
|
||||
|
||||
new_node = await VPCS.instance().duplicate_node(str(node_id), str(destination_node_id))
|
||||
new_node = await VPCS.instance().duplicate_node(node.id, str(destination_node_id))
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_vpcs_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def start_vpcs_node(node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a VPCS node.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.start()
|
||||
await node.start()
|
||||
|
||||
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_vpcs_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_vpcs_node(node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a VPCS node.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop()
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_vpcs_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_vpcs_node(node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a VPCS node.
|
||||
Does nothing, suspend is not supported by VPCS.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload_vpcs_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_vpcs_node(node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Reload a VPCS node.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reload()
|
||||
await node.reload()
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def create_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def create_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Add a NIO (Network Input/Output) to the node.
|
||||
The adapter number on the VPCS node is always 0.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vpcs_manager.create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await vm.port_add_nio_binding(port_number, nio)
|
||||
nio = VPCS.instance().create_nio(jsonable_encoder(nio_data, exclude_unset=True))
|
||||
await node.port_add_nio_binding(port_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.put("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.UDPNIO,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def update_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, nio_data: schemas.UDPNIO):
|
||||
responses=responses)
|
||||
async def update_nio(adapter_number: int, port_number: int, nio_data: schemas.UDPNIO, node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Update a NIO (Network Input/Output) on the node.
|
||||
The adapter number on the VPCS node is always 0.
|
||||
"""
|
||||
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(port_number)
|
||||
nio = node.get_nio(port_number)
|
||||
if nio_data.filters:
|
||||
nio.filters = nio_data.filters
|
||||
await vm.port_update_nio_binding(port_number, nio)
|
||||
await node.port_update_nio_binding(port_number, nio)
|
||||
return nio.__json__()
|
||||
|
||||
|
||||
@router.delete("/{node_id}/adapters/{adapter_number}/ports/{port_number}/nio",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def delete_nio(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def delete_nio(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Delete a NIO (Network Input/Output) from the node.
|
||||
The adapter number on the VPCS node is always 0.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.port_remove_nio_binding(port_number)
|
||||
await node.port_remove_nio_binding(port_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/start_capture",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int, node_capture_data: schemas.NodeCapture):
|
||||
responses=responses)
|
||||
async def start_capture(adapter_number: int,
|
||||
port_number: int,
|
||||
node_capture_data: schemas.NodeCapture,
|
||||
node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Start a packet capture on the node.
|
||||
The adapter number on the VPCS node is always 0.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), node_capture_data.capture_file_name)
|
||||
await node.start_capture(adapter_number, pcap_file_path)
|
||||
return {"pcap_file_path": pcap_file_path}
|
||||
|
||||
|
||||
@router.post("/{node_id}/adapters/{adapter_number}/ports/{port_number}/stop_capture",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_capture(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stop_capture(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Stop a packet capture on the node.
|
||||
The adapter number on the VPCS node is always 0.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.stop_capture(port_number)
|
||||
await node.stop_capture(port_number)
|
||||
|
||||
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console(node: VPCSVM = Depends(dep_node)):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
await vm.reset_console()
|
||||
await node.reset_console()
|
||||
|
||||
|
||||
@router.get("/{node_id}/adapters/{adapter_number}/ports/{port_number}/pcap",
|
||||
responses={404: {"model": schemas.ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stream_pcap_file(project_id: UUID, node_id: UUID, adapter_number: int, port_number: int):
|
||||
responses=responses)
|
||||
async def stream_pcap_file(adapter_number: int, port_number: int, node: VPCSVM = Depends(dep_node)):
|
||||
"""
|
||||
Stream the pcap capture file.
|
||||
The adapter number on the VPCS node is always 0.
|
||||
"""
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(str(node_id), project_id=str(project_id))
|
||||
nio = vm.get_nio(port_number)
|
||||
stream = vpcs_manager.stream_pcap_file(nio, vm.project.id)
|
||||
nio = node.get_nio(port_number)
|
||||
stream = VPCS.instance().stream_pcap_file(nio, node.project.id)
|
||||
return StreamingResponse(stream, media_type="application/vnd.tcpdump.pcap")
|
||||
|
||||
|
||||
|
@ -33,12 +33,12 @@ router = APIRouter()
|
||||
router.include_router(controller.router, tags=["controller"])
|
||||
router.include_router(appliances.router, prefix="/appliances", tags=["appliances"])
|
||||
router.include_router(computes.router, prefix="/computes", tags=["computes"])
|
||||
router.include_router(drawings.router, tags=["drawings"])
|
||||
router.include_router(drawings.router, prefix="/projects/{project_id}/drawings", tags=["drawings"])
|
||||
router.include_router(gns3vm.router, prefix="/gns3vm", tags=["GNS3 VM"])
|
||||
router.include_router(links.router, tags=["links"])
|
||||
router.include_router(nodes.router, tags=["nodes"])
|
||||
router.include_router(links.router, prefix="/projects/{project_id}/links", tags=["links"])
|
||||
router.include_router(nodes.router, prefix="/projects/{project_id}/nodes", tags=["nodes"])
|
||||
router.include_router(notifications.router, prefix="/notifications", tags=["notifications"])
|
||||
router.include_router(projects.router, prefix="/projects", tags=["projects"])
|
||||
router.include_router(snapshots.router, tags=["snapshots"])
|
||||
router.include_router(snapshots.router, prefix="/projects/{project_id}/snapshots", tags=["snapshots"])
|
||||
router.include_router(symbols.router, prefix="/symbols", tags=["symbols"])
|
||||
router.include_router(templates.router, tags=["templates"])
|
||||
|
@ -25,8 +25,11 @@ from typing import Optional
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/", summary="List of appliances")
|
||||
async def list_appliances(update: Optional[bool] = None, symbol_theme: Optional[str] = "Classic"):
|
||||
@router.get("/")
|
||||
async def get_appliances(update: Optional[bool] = None, symbol_theme: Optional[str] = "Classic"):
|
||||
"""
|
||||
Return all appliances known by the controller.
|
||||
"""
|
||||
|
||||
from gns3server.controller import Controller
|
||||
controller = Controller.instance()
|
||||
|
@ -30,9 +30,12 @@ from gns3server.endpoints import schemas
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": ErrorMessage, "description": "Compute not found"}
|
||||
}
|
||||
|
||||
|
||||
@router.post("/",
|
||||
summary="Create a new compute",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Compute,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not connect to compute"},
|
||||
@ -49,14 +52,12 @@ async def create_compute(compute_data: schemas.ComputeCreate):
|
||||
|
||||
|
||||
@router.get("/{compute_id}",
|
||||
summary="Get a compute",
|
||||
response_model=schemas.Compute,
|
||||
response_description="Compute data",
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute not found"}})
|
||||
responses=responses)
|
||||
def get_compute(compute_id: Union[str, UUID]):
|
||||
"""
|
||||
Get compute data from the controller.
|
||||
Return a compute from the controller.
|
||||
"""
|
||||
|
||||
compute = Controller.instance().get_compute(str(compute_id))
|
||||
@ -64,13 +65,11 @@ def get_compute(compute_id: Union[str, UUID]):
|
||||
|
||||
|
||||
@router.get("/",
|
||||
summary="List of all computes",
|
||||
response_model=List[schemas.Compute],
|
||||
response_description="List of computes",
|
||||
response_model_exclude_unset=True)
|
||||
async def list_computes():
|
||||
async def get_computes():
|
||||
"""
|
||||
Return the list of all computes known by the controller.
|
||||
Return all computes known by the controller.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
@ -78,11 +77,9 @@ async def list_computes():
|
||||
|
||||
|
||||
@router.put("/{compute_id}",
|
||||
summary="Update a compute",
|
||||
response_model=schemas.Compute,
|
||||
response_description="Updated compute",
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute not found"}})
|
||||
responses=responses)
|
||||
async def update_compute(compute_id: Union[str, UUID], compute_data: schemas.ComputeUpdate):
|
||||
"""
|
||||
Update a compute on the controller.
|
||||
@ -95,9 +92,8 @@ async def update_compute(compute_id: Union[str, UUID], compute_data: schemas.Com
|
||||
|
||||
|
||||
@router.delete("/{compute_id}",
|
||||
summary="Delete a compute",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
|
||||
responses=responses)
|
||||
async def delete_compute(compute_id: Union[str, UUID]):
|
||||
"""
|
||||
Delete a compute from the controller.
|
||||
@ -107,10 +103,8 @@ async def delete_compute(compute_id: Union[str, UUID]):
|
||||
|
||||
|
||||
@router.get("/{compute_id}/{emulator}/images",
|
||||
summary="List images",
|
||||
response_description="List of images",
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
|
||||
async def list_images(compute_id: Union[str, UUID], emulator: str):
|
||||
responses=responses)
|
||||
async def get_images(compute_id: Union[str, UUID], emulator: str):
|
||||
"""
|
||||
Return the list of images available on a compute for a given emulator type.
|
||||
"""
|
||||
@ -121,23 +115,24 @@ async def list_images(compute_id: Union[str, UUID], emulator: str):
|
||||
|
||||
|
||||
@router.get("/{compute_id}/{emulator}/{endpoint_path:path}",
|
||||
summary="Forward GET request to a compute",
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
|
||||
responses=responses)
|
||||
async def forward_get(compute_id: Union[str, UUID], emulator: str, endpoint_path: str):
|
||||
"""
|
||||
Forward GET request to a compute. Read the full compute API documentation for available endpoints.
|
||||
Forward a GET request to a compute.
|
||||
Read the full compute API documentation for available endpoints.
|
||||
"""
|
||||
|
||||
compute = Controller.instance().get_compute(str(compute_id))
|
||||
result = await compute.forward("GET", emulator, endpoint_path)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/{compute_id}/{emulator}/{endpoint_path:path}",
|
||||
summary="Forward POST request to a compute",
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
|
||||
responses=responses)
|
||||
async def forward_post(compute_id: Union[str, UUID], emulator: str, endpoint_path: str, compute_data: dict):
|
||||
"""
|
||||
Forward POST request to a compute. Read the full compute API documentation for available endpoints.
|
||||
Forward a POST request to a compute.
|
||||
Read the full compute API documentation for available endpoints.
|
||||
"""
|
||||
|
||||
compute = Controller.instance().get_compute(str(compute_id))
|
||||
@ -145,11 +140,11 @@ async def forward_post(compute_id: Union[str, UUID], emulator: str, endpoint_pat
|
||||
|
||||
|
||||
@router.put("/{compute_id}/{emulator}/{endpoint_path:path}",
|
||||
summary="Forward PUT request to a compute",
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
|
||||
responses=responses)
|
||||
async def forward_put(compute_id: Union[str, UUID], emulator: str, endpoint_path: str, compute_data: dict):
|
||||
"""
|
||||
Forward PUT request to a compute. Read the full compute API documentation for available endpoints.
|
||||
Forward a PUT request to a compute.
|
||||
Read the full compute API documentation for available endpoints.
|
||||
"""
|
||||
|
||||
compute = Controller.instance().get_compute(str(compute_id))
|
||||
@ -157,11 +152,10 @@ async def forward_put(compute_id: Union[str, UUID], emulator: str, endpoint_path
|
||||
|
||||
|
||||
@router.post("/{compute_id}/auto_idlepc",
|
||||
summary="Find a new IDLE-PC value",
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
|
||||
responses=responses)
|
||||
async def autoidlepc(compute_id: Union[str, UUID], auto_idle_pc: schemas.AutoIdlePC):
|
||||
"""
|
||||
Find a suitable Idle-PC value for a given IOS image. This may take some time.
|
||||
Find a suitable Idle-PC value for a given IOS image. This may take a few minutes.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
@ -172,12 +166,11 @@ async def autoidlepc(compute_id: Union[str, UUID], auto_idle_pc: schemas.AutoIdl
|
||||
|
||||
|
||||
@router.get("/{compute_id}/ports",
|
||||
summary="Get ports used by a compute",
|
||||
deprecated=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Compute was not found"}})
|
||||
responses=responses)
|
||||
async def ports(compute_id: Union[str, UUID]):
|
||||
"""
|
||||
Get ports information for a given compute.
|
||||
Return ports information for a given compute.
|
||||
"""
|
||||
|
||||
return await Controller.instance().compute_ports(str(compute_id))
|
||||
|
@ -37,7 +37,6 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/shutdown",
|
||||
summary="Shutdown the local server",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={403: {"model": ErrorMessage, "description": "Server shutdown not allowed"}})
|
||||
async def shutdown():
|
||||
@ -75,7 +74,7 @@ async def shutdown():
|
||||
response_model=schemas.Version)
|
||||
def version():
|
||||
"""
|
||||
Retrieve the server version number.
|
||||
Return the server version number.
|
||||
"""
|
||||
|
||||
config = Config.instance()
|
||||
@ -106,7 +105,7 @@ def check_version(version: schemas.Version):
|
||||
response_model=schemas.IOULicense)
|
||||
def get_iou_license():
|
||||
"""
|
||||
Get the IOU license settings
|
||||
Return the IOU license settings
|
||||
"""
|
||||
|
||||
return Controller.instance().iou_license
|
||||
@ -130,7 +129,7 @@ async def update_iou_license(iou_license: schemas.IOULicense):
|
||||
@router.get("/statistics")
|
||||
async def statistics():
|
||||
"""
|
||||
Retrieve server statistics.
|
||||
Return server statistics.
|
||||
"""
|
||||
|
||||
compute_statistics = []
|
||||
|
@ -30,13 +30,15 @@ from gns3server.endpoints.schemas.drawings import Drawing
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": ErrorMessage, "description": "Project or drawing not found"}
|
||||
}
|
||||
|
||||
@router.get("/projects/{project_id}/drawings",
|
||||
summary="List of all drawings",
|
||||
|
||||
@router.get("/",
|
||||
response_model=List[Drawing],
|
||||
response_description="List of drawings",
|
||||
response_model_exclude_unset=True)
|
||||
async def list_drawings(project_id: UUID):
|
||||
async def get_drawings(project_id: UUID):
|
||||
"""
|
||||
Return the list of all drawings for a given project.
|
||||
"""
|
||||
@ -45,27 +47,27 @@ async def list_drawings(project_id: UUID):
|
||||
return [v.__json__() for v in project.drawings.values()]
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/drawings",
|
||||
summary="Create a new drawing",
|
||||
@router.post("/",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Drawing,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
responses=responses)
|
||||
async def create_drawing(project_id: UUID, drawing_data: Drawing):
|
||||
"""
|
||||
Create a new drawing.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
drawing = await project.add_drawing(**jsonable_encoder(drawing_data, exclude_unset=True))
|
||||
return drawing.__json__()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/drawings/{drawing_id}",
|
||||
summary="Get a drawing",
|
||||
@router.get("/{drawing_id}",
|
||||
response_model=Drawing,
|
||||
response_description="Drawing data",
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or drawing not found"}})
|
||||
responses=responses)
|
||||
async def get_drawing(project_id: UUID, drawing_id: UUID):
|
||||
"""
|
||||
Get drawing data for a given project from the controller.
|
||||
Return a drawing.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
@ -73,15 +75,13 @@ async def get_drawing(project_id: UUID, drawing_id: UUID):
|
||||
return drawing.__json__()
|
||||
|
||||
|
||||
@router.put("/projects/{project_id}/drawings/{drawing_id}",
|
||||
summary="Update a drawing",
|
||||
@router.put("/{drawing_id}",
|
||||
response_model=Drawing,
|
||||
response_description="Updated drawing",
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or drawing not found"}})
|
||||
responses=responses)
|
||||
async def update_drawing(project_id: UUID, drawing_id: UUID, drawing_data: Drawing):
|
||||
"""
|
||||
Update a drawing for a given project on the controller.
|
||||
Update a drawing.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
@ -90,13 +90,12 @@ async def update_drawing(project_id: UUID, drawing_id: UUID, drawing_data: Drawi
|
||||
return drawing.__json__()
|
||||
|
||||
|
||||
@router.delete("/projects/{project_id}/drawings/{drawing_id}",
|
||||
summary="Delete a drawing",
|
||||
@router.delete("/{drawing_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or drawing not found"}})
|
||||
responses=responses)
|
||||
async def delete_drawing(project_id: UUID, drawing_id: UUID):
|
||||
"""
|
||||
Update a drawing for a given project from the controller.
|
||||
Delete a drawing.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
|
@ -28,9 +28,8 @@ from gns3server.endpoints.schemas.gns3vm import GNS3VM
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/engines",
|
||||
summary="List of engines")
|
||||
async def list_engines():
|
||||
@router.get("/engines")
|
||||
async def get_engines():
|
||||
"""
|
||||
Return the list of supported engines for the GNS3VM.
|
||||
"""
|
||||
@ -39,11 +38,10 @@ async def list_engines():
|
||||
return gns3_vm.engine_list()
|
||||
|
||||
|
||||
@router.get("/engines/{engine}/vms",
|
||||
summary="List of VMs")
|
||||
@router.get("/engines/{engine}/vms")
|
||||
async def get_vms(engine: str):
|
||||
"""
|
||||
Get all the available VMs for a specific virtualization engine.
|
||||
Return all the available VMs for a specific virtualization engine.
|
||||
"""
|
||||
|
||||
vms = await Controller.instance().gns3vm.list(engine)
|
||||
@ -51,19 +49,22 @@ async def get_vms(engine: str):
|
||||
|
||||
|
||||
@router.get("/",
|
||||
summary="Get GNS3 VM settings",
|
||||
response_model=GNS3VM)
|
||||
async def get_gns3vm_settings():
|
||||
"""
|
||||
Return the GNS3 VM settings.
|
||||
"""
|
||||
|
||||
return Controller.instance().gns3vm.__json__()
|
||||
|
||||
|
||||
@router.put("/",
|
||||
summary="Update GNS3 VM settings",
|
||||
response_model=GNS3VM,
|
||||
response_description="Updated GNS3 VM settings",
|
||||
response_model_exclude_unset=True)
|
||||
async def update_gns3vm_settings(gns3vm_data: GNS3VM):
|
||||
"""
|
||||
Update the GNS3 VM settings.
|
||||
"""
|
||||
|
||||
controller = Controller().instance()
|
||||
gns3_vm = controller.gns3vm
|
||||
|
@ -19,7 +19,7 @@
|
||||
API endpoints for links.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, status
|
||||
from fastapi import APIRouter, Depends, Request, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse
|
||||
from typing import List
|
||||
@ -27,36 +27,47 @@ from uuid import UUID
|
||||
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.controller.controller_error import ControllerError
|
||||
from gns3server.controller.link import Link
|
||||
from gns3server.endpoints.schemas.common import ErrorMessage
|
||||
from gns3server.endpoints.schemas.links import Link
|
||||
|
||||
import aiohttp
|
||||
import multidict
|
||||
|
||||
from gns3server.endpoints import schemas
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": ErrorMessage, "description": "Could not find project or link"}
|
||||
}
|
||||
|
||||
@router.get("/projects/{project_id}/links",
|
||||
summary="List of all links",
|
||||
response_model=List[Link],
|
||||
response_description="List of links",
|
||||
|
||||
async def dep_link(project_id: UUID, link_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
link = project.get_link(str(link_id))
|
||||
return link
|
||||
|
||||
|
||||
@router.get("/",
|
||||
response_model=List[schemas.Link],
|
||||
response_model_exclude_unset=True)
|
||||
async def list_links(project_id: UUID):
|
||||
async def get_links(project_id: UUID):
|
||||
"""
|
||||
Return all links for a given project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
return [v.__json__() for v in project.links.values()]
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/links",
|
||||
summary="Create a new link",
|
||||
@router.post("/",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Link,
|
||||
response_model=schemas.Link,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"},
|
||||
409: {"model": ErrorMessage, "description": "Could not create link"}})
|
||||
async def create_link(project_id: UUID, link_data: Link):
|
||||
async def create_link(project_id: UUID, link_data: schemas.Link):
|
||||
"""
|
||||
Create a new link on the controller.
|
||||
Create a new link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
@ -78,42 +89,37 @@ async def create_link(project_id: UUID, link_data: Link):
|
||||
return link.__json__()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/links/{link_id}/available_filters",
|
||||
summary="List of filters",
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or link"}})
|
||||
async def list_filters(project_id: UUID, link_id: UUID):
|
||||
@router.get("/{link_id}/available_filters",
|
||||
responses=responses)
|
||||
async def get_filters(link: Link = Depends(dep_link)):
|
||||
"""
|
||||
Return the list of filters available for this link.
|
||||
Return all filters available for a given link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
link = project.get_link(str(link_id))
|
||||
return link.available_filters()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/links/{link_id}",
|
||||
summary="Get a link",
|
||||
response_model=Link,
|
||||
response_description="Link data",
|
||||
@router.get("/{link_id}",
|
||||
response_model=schemas.Link,
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or link"}})
|
||||
async def get_link(project_id: UUID, link_id: UUID):
|
||||
responses=responses)
|
||||
async def get_link(link: Link = Depends(dep_link)):
|
||||
"""
|
||||
Return a link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
link = project.get_link(str(link_id))
|
||||
return link.__json__()
|
||||
|
||||
|
||||
@router.put("/projects/{project_id}/links/{link_id}",
|
||||
summary="Update a link",
|
||||
response_model=Link,
|
||||
response_description="Updated link",
|
||||
@router.put("/{link_id}",
|
||||
response_model=schemas.Link,
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
|
||||
async def update_link(project_id: UUID, link_id: UUID, link_data: Link):
|
||||
responses=responses)
|
||||
async def update_link(link_data: schemas.Link, link: Link = Depends(dep_link)):
|
||||
"""
|
||||
Update a link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
link = project.get_link(str(link_id))
|
||||
link_data = jsonable_encoder(link_data, exclude_unset=True)
|
||||
if "filters" in link_data:
|
||||
await link.update_filters(link_data["filters"])
|
||||
@ -124,60 +130,53 @@ async def update_link(project_id: UUID, link_id: UUID, link_data: Link):
|
||||
return link.__json__()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/links/{link_id}/start_capture",
|
||||
summary="Start a packet capture",
|
||||
@router.post("/{link_id}/start_capture",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Link,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
|
||||
async def start_capture(project_id: UUID, link_id: UUID, capture_data: dict):
|
||||
response_model=schemas.Link,
|
||||
responses=responses)
|
||||
async def start_capture(capture_data: dict, link: Link = Depends(dep_link)):
|
||||
"""
|
||||
Start packet capture on the link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
link = project.get_link(str(link_id))
|
||||
await link.start_capture(data_link_type=capture_data.get("data_link_type", "DLT_EN10MB"),
|
||||
capture_file_name=capture_data.get("capture_file_name"))
|
||||
return link.__json__()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/links/{link_id}/stop_capture",
|
||||
summary="Stop a packet capture",
|
||||
@router.post("/{link_id}/stop_capture",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=Link,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
|
||||
async def stop_capture(project_id: UUID, link_id: UUID):
|
||||
response_model=schemas.Link,
|
||||
responses=responses)
|
||||
async def stop_capture(link: Link = Depends(dep_link)):
|
||||
"""
|
||||
Stop packet capture on the link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
link = project.get_link(str(link_id))
|
||||
await link.stop_capture()
|
||||
return link.__json__()
|
||||
|
||||
|
||||
@router.delete("/projects/{project_id}/links/{link_id}",
|
||||
summary="Delete a link",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
|
||||
async def delete(project_id: UUID, link_id: UUID):
|
||||
@router.delete("/{link_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses=responses)
|
||||
async def delete_link(project_id: UUID, link: Link = Depends(dep_link)):
|
||||
"""
|
||||
Delete link from the project.
|
||||
Delete a link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
await project.delete_link(str(link_id))
|
||||
await project.delete_link(link.id)
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/links/{link_id}/reset",
|
||||
summary="Reset a link",
|
||||
response_model=Link,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or link not found"}})
|
||||
async def reset(project_id: UUID, link_id: UUID):
|
||||
@router.post("/{link_id}/reset",
|
||||
response_model=schemas.Link,
|
||||
responses=responses)
|
||||
async def reset_link(link: Link = Depends(dep_link)):
|
||||
"""
|
||||
Reset a link.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
link = project.get_link(str(link_id))
|
||||
await link.reset()
|
||||
return link.__json__()
|
||||
|
||||
|
@ -21,28 +21,28 @@ API endpoints for nodes.
|
||||
|
||||
import asyncio
|
||||
|
||||
from fastapi import APIRouter, Request, Response, status
|
||||
from fastapi import APIRouter, Depends, Request, Response, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.routing import APIRoute
|
||||
from typing import List, Callable
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.controller.node import Node
|
||||
from gns3server.controller.project import Project
|
||||
from gns3server.utils import force_unix_path
|
||||
from gns3server.controller.controller_error import ControllerForbiddenError
|
||||
from gns3server.endpoints.schemas.common import ErrorMessage
|
||||
from gns3server.endpoints import schemas
|
||||
|
||||
import aiohttp
|
||||
|
||||
node_locks = {}
|
||||
|
||||
|
||||
class NodeConcurrency(APIRoute):
|
||||
"""
|
||||
To avoid strange effect we prevent concurrency
|
||||
To avoid strange effects, we prevent concurrency
|
||||
between the same instance of the node
|
||||
(excepting when streaming a PCAP file and WebSocket consoles).
|
||||
(excepting when streaming a PCAP file and for WebSocket consoles).
|
||||
"""
|
||||
|
||||
def get_route_handler(self) -> Callable:
|
||||
@ -74,25 +74,41 @@ class NodeConcurrency(APIRoute):
|
||||
|
||||
router = APIRouter(route_class=NodeConcurrency)
|
||||
|
||||
# # dependency to retrieve a node
|
||||
# async def get_node(project_id: UUID, node_id: UUID):
|
||||
#
|
||||
# project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
# node = project.get_node(str(node_id))
|
||||
# return node
|
||||
responses = {
|
||||
404: {"model": ErrorMessage, "description": "Could not find project or node"}
|
||||
}
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes",
|
||||
summary="Create a new node",
|
||||
async def dep_project(project_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
return project
|
||||
|
||||
|
||||
async def dep_node(node_id: UUID, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Dependency to retrieve a node.
|
||||
"""
|
||||
|
||||
node = project.get_node(str(node_id))
|
||||
return node
|
||||
|
||||
|
||||
@router.post("/",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Node,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"},
|
||||
409: {"model": ErrorMessage, "description": "Could not create node"}})
|
||||
async def create_node(project_id: UUID, node_data: schemas.Node):
|
||||
async def create_node(node_data: schemas.Node, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Create a new node.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(str(node_data.compute_id))
|
||||
project = await controller.get_loaded_project(str(project_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
node = await project.add_node(compute,
|
||||
node_data.pop("name"),
|
||||
@ -101,91 +117,82 @@ async def create_node(project_id: UUID, node_data: schemas.Node):
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/nodes",
|
||||
summary="List of all nodes",
|
||||
@router.get("/",
|
||||
response_model=List[schemas.Node],
|
||||
response_description="List of nodes",
|
||||
response_model_exclude_unset=True)
|
||||
async def list_nodes(project_id: UUID):
|
||||
async def get_nodes(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Return all nodes belonging to a given project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
return [v.__json__() for v in project.nodes.values()]
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/start",
|
||||
summary="Start all nodes",
|
||||
@router.post("/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def start_all_nodes(project_id: UUID):
|
||||
responses=responses)
|
||||
async def start_all_nodes(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Start all nodes belonging to the project
|
||||
Start all nodes belonging to a given project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
await project.start_all()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/stop",
|
||||
summary="Stop all nodes",
|
||||
@router.post("/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def stop_all_nodes(project_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_all_nodes(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Stop all nodes belonging to the project
|
||||
Stop all nodes belonging to a given project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
await project.stop_all()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/suspend",
|
||||
summary="Stop all nodes",
|
||||
@router.post("/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def suspend_all_nodes(project_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_all_nodes(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Suspend all nodes belonging to the project
|
||||
Suspend all nodes belonging to a given project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
await project.suspend_all()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/reload",
|
||||
summary="Reload all nodes",
|
||||
@router.post("/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def reload_all_nodes(project_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_all_nodes(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Reload all nodes belonging to the project
|
||||
Reload all nodes belonging to a given project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
await project.stop_all()
|
||||
await project.start_all()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/nodes/{node_id}",
|
||||
summary="Get a node",
|
||||
@router.get("/{node_id}",
|
||||
response_model=schemas.Node,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
def get_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
def get_node(node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Return a node from a given project.
|
||||
"""
|
||||
|
||||
project = Controller.instance().get_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.put("/projects/{project_id}/nodes/{node_id}",
|
||||
summary="Update a node",
|
||||
@router.put("/{node_id}",
|
||||
response_model=schemas.Node,
|
||||
response_description="Updated node",
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Project or node not found"}})
|
||||
async def update_node(project_id: UUID, node_id: UUID, node_data: schemas.NodeUpdate):
|
||||
responses=responses)
|
||||
async def update_node(node_data: schemas.NodeUpdate, node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Update a node.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
node_data = jsonable_encoder(node_data, exclude_unset=True)
|
||||
|
||||
# Ignore these because we only use them when creating a node
|
||||
@ -197,139 +204,129 @@ async def update_node(project_id: UUID, node_id: UUID, node_data: schemas.NodeUp
|
||||
return node.__json__()
|
||||
|
||||
|
||||
@router.delete("/projects/{project_id}/nodes/{node_id}",
|
||||
summary="Delete a node",
|
||||
@router.delete("/{node_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"},
|
||||
responses={**responses,
|
||||
409: {"model": ErrorMessage, "description": "Cannot delete node"}})
|
||||
async def delete_node(project_id: UUID, node_id: UUID):
|
||||
async def delete_node(node_id: UUID, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Delete a node from a project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
await project.delete_node(str(node_id))
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/duplicate",
|
||||
summary="Duplicate a node",
|
||||
@router.post("/{node_id}/duplicate",
|
||||
response_model=schemas.Node,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def duplicate_node(project_id: UUID, node_id: UUID, duplicate_data: schemas.NodeDuplicate):
|
||||
responses=responses)
|
||||
async def duplicate_node(duplicate_data: schemas.NodeDuplicate, node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Duplicate a node.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
new_node = await project.duplicate_node(node,
|
||||
duplicate_data.x,
|
||||
duplicate_data.y,
|
||||
duplicate_data.z)
|
||||
new_node = await node.project.duplicate_node(node,
|
||||
duplicate_data.x,
|
||||
duplicate_data.y,
|
||||
duplicate_data.z)
|
||||
return new_node.__json__()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/start",
|
||||
summary="Start a node",
|
||||
@router.post("/{node_id}/start",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def start_node(project_id: UUID, node_id: UUID, start_data: dict):
|
||||
responses=responses)
|
||||
async def start_node(start_data: dict, node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Start a node.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
await node.start(data=start_data)
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/stop",
|
||||
summary="Stop a node",
|
||||
@router.post("/{node_id}/stop",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def stop_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def stop_node(node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Stop a node.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
await node.stop()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/suspend",
|
||||
summary="Suspend a node",
|
||||
@router.post("/{node_id}/suspend",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def suspend_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def suspend_node(node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Suspend a node.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
await node.suspend()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/reload",
|
||||
summary="Reload a node",
|
||||
@router.post("/{node_id}/reload",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reload_node(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def reload_node(node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Reload a node.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
await node.reload()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/nodes/{node_id}/links",
|
||||
summary="List of all node links",
|
||||
@router.get("/{node_id}/links",
|
||||
response_model=List[schemas.Link],
|
||||
response_description="List of links",
|
||||
response_model_exclude_unset=True)
|
||||
async def node_links(project_id: UUID, node_id: UUID):
|
||||
async def get_node_links(node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Return all the links connected to the node.
|
||||
Return all the links connected to a node.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
links = []
|
||||
for link in node.links:
|
||||
links.append(link.__json__())
|
||||
return links
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/nodes/{node_id}/dynamips/auto_idlepc",
|
||||
summary="Compute an Idle-PC",
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def auto_idlepc(project_id: UUID, node_id: UUID):
|
||||
@router.get("/{node_id}/dynamips/auto_idlepc",
|
||||
responses=responses)
|
||||
async def auto_idlepc(node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Compute an Idle-PC value for a Dynamips node
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
return await node.dynamips_auto_idlepc()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/nodes/{node_id}/dynamips/idlepc_proposals",
|
||||
summary="Compute list of Idle-PC values",
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def idlepc_proposals(project_id: UUID, node_id: UUID):
|
||||
@router.get("/{node_id}/dynamips/idlepc_proposals",
|
||||
responses=responses)
|
||||
async def idlepc_proposals(node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Compute a list of potential idle-pc values for a Dynamips node
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
return await node.dynamips_idlepc_proposals()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/resize_disk",
|
||||
summary="Resize a disk",
|
||||
@router.post("/{node_id}/resize_disk",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def resize_disk(project_id: UUID, node_id: UUID, resize_data: dict):
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
responses=responses)
|
||||
async def resize_disk(resize_data: dict, node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Resize a disk image.
|
||||
"""
|
||||
await node.post("/resize_disk", **resize_data)
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/nodes/{node_id}/files/{file_path:path}",
|
||||
summary="Get a file in the node directory",
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def get_file(project_id: UUID, node_id: UUID, file_path: str):
|
||||
@router.get("/{node_id}/files/{file_path:path}",
|
||||
responses=responses)
|
||||
async def get_file(file_path: str, node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Return a file in the node directory
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
path = force_unix_path(file_path)
|
||||
|
||||
# Raise error if user try to escape
|
||||
@ -339,18 +336,20 @@ async def get_file(project_id: UUID, node_id: UUID, file_path: str):
|
||||
node_type = node.node_type
|
||||
path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
|
||||
|
||||
res = await node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True)
|
||||
res = await node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=node.project.id, path=path),
|
||||
timeout=None,
|
||||
raw=True)
|
||||
return Response(res.body, media_type="application/octet-stream")
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/files/{file_path:path}",
|
||||
summary="Write a file in the node directory",
|
||||
@router.post("/{node_id}/files/{file_path:path}",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def post_file(project_id: UUID, node_id: UUID, file_path: str, request: Request):
|
||||
responses=responses)
|
||||
async def post_file(file_path: str, request: Request, node: Node = Depends(dep_node)):
|
||||
"""
|
||||
Write a file in the node directory.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
path = force_unix_path(file_path)
|
||||
|
||||
# Raise error if user try to escape
|
||||
@ -362,7 +361,10 @@ async def post_file(project_id: UUID, node_id: UUID, file_path: str, request: Re
|
||||
|
||||
data = await request.body() #FIXME: are we handling timeout or large files correctly?
|
||||
|
||||
await node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True)
|
||||
await node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=node.project.id, path=path),
|
||||
data=data,
|
||||
timeout=None,
|
||||
raw=True)
|
||||
|
||||
|
||||
# @Route.get(
|
||||
@ -420,23 +422,20 @@ async def post_file(project_id: UUID, node_id: UUID, file_path: str, request: Re
|
||||
# return ws
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/console/reset",
|
||||
@router.post("/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def reset_console_all(project_id: UUID):
|
||||
responses=responses)
|
||||
async def reset_console_all(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Reset console for all nodes belonging to the project.
|
||||
"""
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
await project.reset_console_all()
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/nodes/{node_id}/console/reset",
|
||||
@router.post("/{node_id}/console/reset",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or node"}})
|
||||
async def console_reset(project_id: UUID, node_id: UUID):
|
||||
responses=responses)
|
||||
async def console_reset(node: Node = Depends(dep_node)):
|
||||
|
||||
project = await Controller.instance().get_loaded_project(str(project_id))
|
||||
node = project.get_node(str(node_id))
|
||||
await node.post("/console/reset")#, request.json)
|
||||
|
@ -19,28 +19,7 @@
|
||||
API endpoints for projects.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Body, HTTPException, status, WebSocket, WebSocketDisconnect
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse, FileResponse
|
||||
from websockets.exceptions import WebSocketException
|
||||
from typing import List
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.endpoints.schemas.common import ErrorMessage
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.controller.controller_error import ControllerError, ControllerForbiddenError
|
||||
from gns3server.controller.import_project import import_project as import_controller_project
|
||||
from gns3server.controller.export_project import export_project as export_controller_project
|
||||
from gns3server.utils.asyncio import aiozipstream
|
||||
from gns3server.config import Config
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
import os
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import tempfile
|
||||
import zipfile
|
||||
@ -50,17 +29,52 @@ import time
|
||||
import logging
|
||||
log = logging.getLogger()
|
||||
|
||||
from fastapi import APIRouter, Depends, Request, Body, HTTPException, status, WebSocket, WebSocketDisconnect
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import StreamingResponse, FileResponse
|
||||
from websockets.exceptions import WebSocketException
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.endpoints.schemas.common import ErrorMessage
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.controller.project import Project
|
||||
from gns3server.controller.controller_error import ControllerError, ControllerForbiddenError
|
||||
from gns3server.controller.import_project import import_project as import_controller_project
|
||||
from gns3server.controller.export_project import export_project as export_controller_project
|
||||
from gns3server.utils.asyncio import aiozipstream
|
||||
from gns3server.config import Config
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
responses = {
|
||||
404: {"model": ErrorMessage, "description": "Could not find project"}
|
||||
}
|
||||
|
||||
|
||||
def dep_project(project_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a project.
|
||||
"""
|
||||
|
||||
project = Controller.instance().get_project(str(project_id))
|
||||
return project
|
||||
|
||||
|
||||
CHUNK_SIZE = 1024 * 8 # 8KB
|
||||
|
||||
|
||||
@router.post("/",
|
||||
summary="Create project",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Project,
|
||||
response_model_exclude_unset=True,
|
||||
responses={409: {"model": ErrorMessage, "description": "Could not create project"}})
|
||||
async def create_project(project_data: schemas.ProjectCreate):
|
||||
"""
|
||||
Create a new project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = await controller.add_project(**jsonable_encoder(project_data, exclude_unset=True))
|
||||
@ -69,92 +83,105 @@ async def create_project(project_data: schemas.ProjectCreate):
|
||||
|
||||
|
||||
@router.get("/",
|
||||
summary="List of all projects",
|
||||
response_model=List[schemas.Project],
|
||||
response_description="List of projects",
|
||||
response_model_exclude_unset=True)
|
||||
def list_projects():
|
||||
def get_projects():
|
||||
"""
|
||||
Return all projects.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
return [p.__json__() for p in controller.projects.values()]
|
||||
|
||||
|
||||
@router.get("/{project_id}",
|
||||
summary="Get a project",
|
||||
response_model=schemas.Project,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
def get_project(project_id: UUID):
|
||||
responses=responses)
|
||||
def get_project(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Return a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
return project.__json__()
|
||||
|
||||
|
||||
@router.put("/{project_id}",
|
||||
summary="Update a project",
|
||||
response_model=schemas.Project,
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def update_project(project_id: UUID, project_data: schemas.ProjectUpdate):
|
||||
responses=responses)
|
||||
async def update_project(project_data: schemas.ProjectUpdate, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Update a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
await project.update(**jsonable_encoder(project_data, exclude_unset=True))
|
||||
return project.__json__()
|
||||
|
||||
|
||||
@router.delete("/{project_id}",
|
||||
summary="Delete a project",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def delete_project(project_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_project(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Delete a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
await project.delete()
|
||||
controller.remove_project(project)
|
||||
|
||||
|
||||
@router.get("/{project_id}/stats",
|
||||
summary="Get a project statistics",
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
def get_project_stats(project_id: UUID):
|
||||
responses=responses)
|
||||
def get_project_stats(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Return a project statistics.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
return project.stats()
|
||||
|
||||
|
||||
@router.post("/{project_id}/close",
|
||||
summary="Close a project",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={409: {"model": ErrorMessage, "description": "Could not create project"}})
|
||||
async def close_project(project_id: UUID):
|
||||
responses={
|
||||
**responses,
|
||||
409: {"model": ErrorMessage, "description": "Could not close project"}
|
||||
})
|
||||
async def close_project(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Close a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
await project.close()
|
||||
|
||||
|
||||
@router.post("/{project_id}/open",
|
||||
summary="Open a project",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Project,
|
||||
responses={409: {"model": ErrorMessage, "description": "Could not create project"}})
|
||||
async def open_project(project_id: UUID):
|
||||
responses={
|
||||
**responses,
|
||||
409: {"model": ErrorMessage, "description": "Could not open project"}
|
||||
})
|
||||
async def open_project(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Open a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
await project.open()
|
||||
return project.__json__()
|
||||
|
||||
|
||||
@router.post("/load",
|
||||
summary="Open a project (local server only)",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Project,
|
||||
responses={409: {"model": ErrorMessage, "description": "Could not load project"}})
|
||||
responses={
|
||||
**responses,
|
||||
409: {"model": ErrorMessage, "description": "Could not load project"}
|
||||
})
|
||||
async def load_project(path: str = Body(..., embed=True)):
|
||||
"""
|
||||
Load a project (local server only).
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
config = Config.instance()
|
||||
@ -224,9 +251,8 @@ async def notification_ws(project_id: UUID, websocket: WebSocket):
|
||||
|
||||
|
||||
@router.get("/{project_id}/export",
|
||||
summary="Export project",
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def export_project(project_id: UUID,
|
||||
responses=responses)
|
||||
async def export_project(project: Project = Depends(dep_project),
|
||||
include_snapshots: bool = False,
|
||||
include_images: bool = False,
|
||||
reset_mac_addresses: bool = False,
|
||||
@ -235,9 +261,6 @@ async def export_project(project_id: UUID,
|
||||
Export a project as a portable archive.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
|
||||
compression_query = compression.lower()
|
||||
if compression_query == "zip":
|
||||
compression = zipfile.ZIP_DEFLATED
|
||||
@ -277,10 +300,9 @@ async def export_project(project_id: UUID,
|
||||
|
||||
|
||||
@router.post("/{project_id}/import",
|
||||
summary="Import a project",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Project,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
responses=responses)
|
||||
async def import_project(project_id: UUID, request: Request):
|
||||
"""
|
||||
Import a project from a portable archive.
|
||||
@ -319,15 +341,16 @@ async def import_project(project_id: UUID, request: Request):
|
||||
|
||||
|
||||
@router.post("/{project_id}/duplicate",
|
||||
summary="Duplicate a project",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Project,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"},
|
||||
409: {"model": ErrorMessage, "description": "Could not duplicate project"}})
|
||||
async def duplicate(project_id: UUID, project_data: schemas.ProjectDuplicate):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = await controller.get_loaded_project(str(project_id))
|
||||
responses={
|
||||
**responses,
|
||||
409: {"model": ErrorMessage, "description": "Could not duplicate project"}
|
||||
})
|
||||
async def duplicate(project_data: schemas.ProjectDuplicate, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Duplicate a project.
|
||||
"""
|
||||
|
||||
if project_data.path:
|
||||
config = Config.instance()
|
||||
@ -343,13 +366,11 @@ async def duplicate(project_id: UUID, project_data: schemas.ProjectDuplicate):
|
||||
|
||||
|
||||
@router.get("/{project_id}/files/{file_path:path}")
|
||||
async def get_file(project_id: UUID, file_path: str):
|
||||
async def get_file(file_path: str, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Get a file from a project.
|
||||
Return a file from a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = await controller.get_loaded_project(str(project_id))
|
||||
path = os.path.normpath(file_path).strip('/')
|
||||
|
||||
# Raise error if user try to escape
|
||||
@ -365,10 +386,11 @@ async def get_file(project_id: UUID, file_path: str):
|
||||
|
||||
@router.post("/{project_id}/files/{file_path:path}",
|
||||
status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def write_file(project_id: UUID, file_path: str, request: Request):
|
||||
async def write_file(file_path: str, request: Request, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Write a file from a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = await controller.get_loaded_project(str(project_id))
|
||||
path = os.path.normpath(file_path).strip("/")
|
||||
|
||||
# Raise error if user try to escape
|
||||
|
@ -19,75 +19,81 @@
|
||||
API endpoints for snapshots.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, status
|
||||
|
||||
import logging
|
||||
log = logging.getLogger()
|
||||
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
from gns3server.controller.project import Project
|
||||
from gns3server.endpoints.schemas.common import ErrorMessage
|
||||
from gns3server.endpoints import schemas
|
||||
from gns3server.controller import Controller
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
import logging
|
||||
log = logging.getLogger()
|
||||
responses = {
|
||||
404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}
|
||||
}
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/snapshots",
|
||||
def dep_project(project_id: UUID):
|
||||
"""
|
||||
Dependency to retrieve a project.
|
||||
"""
|
||||
|
||||
project = Controller.instance().get_project(str(project_id))
|
||||
return project
|
||||
|
||||
|
||||
@router.post("/",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Snapshot,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
async def create_snapshot(project_id: UUID, snapshot_data: schemas.SnapshotCreate):
|
||||
responses=responses)
|
||||
async def create_snapshot(snapshot_data: schemas.SnapshotCreate, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Create a new snapshot of the project.
|
||||
Create a new snapshot of a project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
snapshot = await project.snapshot(snapshot_data.name)
|
||||
return snapshot.__json__()
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/snapshots",
|
||||
@router.get("/",
|
||||
response_model=List[schemas.Snapshot],
|
||||
response_description="List of snapshots",
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project"}})
|
||||
def list_snapshots(project_id: UUID):
|
||||
responses=responses)
|
||||
def get_snapshots(project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Return a list of snapshots belonging to the project.
|
||||
Return all snapshots belonging to a given project.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
snapshots = [s for s in project.snapshots.values()]
|
||||
return [s.__json__() for s in sorted(snapshots, key=lambda s: (s.created_at, s.name))]
|
||||
|
||||
|
||||
@router.delete("/projects/{project_id}/snapshots/{snapshot_id}",
|
||||
@router.delete("/{snapshot_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}})
|
||||
async def delete_snapshot(project_id: UUID, snapshot_id: UUID):
|
||||
responses=responses)
|
||||
async def delete_snapshot(snapshot_id: UUID, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Delete a snapshot belonging to the project.
|
||||
Delete a snapshot.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
await project.delete_snapshot(str(snapshot_id))
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/snapshots/{snapshot_id}/restore",
|
||||
@router.post("/{snapshot_id}/restore",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Project,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or snapshot"}})
|
||||
async def restore_snapshot(project_id: UUID, snapshot_id: UUID):
|
||||
responses=responses)
|
||||
async def restore_snapshot(snapshot_id: UUID, project: Project = Depends(dep_project)):
|
||||
"""
|
||||
Restore a snapshot from the project.
|
||||
Restore a snapshot.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
snapshot = project.get_snapshot(str(snapshot_id))
|
||||
project = await snapshot.restore()
|
||||
return project.__json__()
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2016 GNS3 Technologies Inc.
|
||||
# Copyright (C) 2020 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -20,9 +20,8 @@ API endpoints for symbols.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from fastapi import APIRouter, Request, status, File, UploadFile
|
||||
from fastapi import APIRouter, Request, status
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from gns3server.controller import Controller
|
||||
@ -37,7 +36,7 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/")
|
||||
def list_symbols():
|
||||
def get_symbols():
|
||||
|
||||
controller = Controller.instance()
|
||||
return controller.symbols.list()
|
||||
@ -46,6 +45,9 @@ def list_symbols():
|
||||
@router.get("/{symbol_id:path}/raw",
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find symbol"}})
|
||||
async def get_symbol(symbol_id: str):
|
||||
"""
|
||||
Download a symbol file.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
try:
|
||||
@ -76,9 +78,9 @@ async def upload_symbol(symbol_id: str, request: Request):
|
||||
|
||||
|
||||
@router.get("/default_symbols")
|
||||
def list_default_symbols():
|
||||
def get_default_symbols():
|
||||
"""
|
||||
Return list of default symbols.
|
||||
Return all default symbols.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
|
@ -22,6 +22,9 @@ API endpoints for templates.
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from fastapi import APIRouter, Request, Response, HTTPException, status
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from typing import Union, List
|
||||
@ -33,8 +36,10 @@ from gns3server.controller import Controller
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
responses = {
|
||||
404: {"model": ErrorMessage, "description": "Could not find template"}
|
||||
}
|
||||
|
||||
|
||||
#template_create_models = Union[schemas.VPCSTemplateCreate, schemas.CloudTemplateCreate, schemas.IOUTemplateCreate]
|
||||
#template_update_models = Union[schemas.VPCSTemplateUpdate, schemas.CloudTemplateUpdate, schemas.IOUTemplateUpdate]
|
||||
@ -45,6 +50,9 @@ log = logging.getLogger(__name__)
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=schemas.Template)
|
||||
def create_template(template_data: schemas.TemplateCreate):
|
||||
"""
|
||||
Create a new template.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
template = controller.template_manager.add_template(jsonable_encoder(template_data, exclude_unset=True))
|
||||
@ -54,11 +62,13 @@ def create_template(template_data: schemas.TemplateCreate):
|
||||
|
||||
|
||||
@router.get("/templates/{template_id}",
|
||||
summary="List of all nodes",
|
||||
response_model=schemas.Template,
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find template"}})
|
||||
responses=responses)
|
||||
def get_template(template_id: UUID, request: Request, response: Response):
|
||||
"""
|
||||
Return a template.
|
||||
"""
|
||||
|
||||
request_etag = request.headers.get("If-None-Match", "")
|
||||
controller = Controller.instance()
|
||||
@ -75,8 +85,11 @@ def get_template(template_id: UUID, request: Request, response: Response):
|
||||
@router.put("/templates/{template_id}",
|
||||
response_model=schemas.Template,
|
||||
response_model_exclude_unset=True,
|
||||
responses={404: {"model": ErrorMessage, "description": "Template not found"}})
|
||||
responses=responses)
|
||||
def update_template(template_id: UUID, template_data: schemas.TemplateUpdate):
|
||||
"""
|
||||
Update a template.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
template = controller.template_manager.get_template(str(template_id))
|
||||
@ -86,8 +99,11 @@ def update_template(template_id: UUID, template_data: schemas.TemplateUpdate):
|
||||
|
||||
@router.delete("/templates/{template_id}",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find template"}})
|
||||
responses=responses)
|
||||
def delete_template(template_id: UUID):
|
||||
"""
|
||||
Delete a template.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
controller.template_manager.delete_template(str(template_id))
|
||||
@ -95,9 +111,11 @@ def delete_template(template_id: UUID):
|
||||
|
||||
@router.get("/templates",
|
||||
response_model=List[schemas.Template],
|
||||
response_description="List of templates",
|
||||
response_model_exclude_unset=True)
|
||||
def list_templates():
|
||||
def get_templates():
|
||||
"""
|
||||
Return all templates.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
return [c.__json__() for c in controller.template_manager.templates.values()]
|
||||
@ -106,8 +124,11 @@ def list_templates():
|
||||
@router.post("/templates/{template_id}/duplicate",
|
||||
response_model=schemas.Template,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find template"}})
|
||||
responses=responses)
|
||||
async def duplicate_template(template_id: UUID):
|
||||
"""
|
||||
Duplicate a template.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
template = controller.template_manager.duplicate_template(str(template_id))
|
||||
@ -119,6 +140,9 @@ async def duplicate_template(template_id: UUID):
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
responses={404: {"model": ErrorMessage, "description": "Could not find project or template"}})
|
||||
async def create_node_from_template(project_id: UUID, template_id: UUID, template_usage: schemas.TemplateUsage):
|
||||
"""
|
||||
Create a new node from a template.
|
||||
"""
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(str(project_id))
|
||||
|
Loading…
Reference in New Issue
Block a user