mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
perf: batch NIO dispatch on project open (one HTTP per compute)
Project open used to create each link by issuing two NIO POSTs from the
controller to the compute — ~5000 HTTP round-trips for a 2500-link
topology, all funnelling through the single shared controller/compute
event loop and capping throughput near 12 links/s.
Replace it with a bulk path:
- UDPLink split into _prepare() (local: ports, peer addrs, link_data)
and _commit_nios() (dispatch). create() = prepare + commit (interactive).
- Link.add_node gains batch=True: attach both nodes without triggering
per-link NIO HTTP.
- compute: new POST /projects/{id}/nios/batch endpoint with a unified
_add_nio_binding dispatch across node types (docker/qemu/iou/vpcs/
builtin differ in signature).
- project.open: prepare all links locally, group NIO entries by compute,
send each compute a single /nios/batch, then finalise (wire node/port
refs, mark created, notify, apply marker defs) in parallel.
Cuts controller->compute HTTP from O(links) to O(computes). Test added
for the batch endpoint.
This commit is contained in:
7 files changed
+280
-29
No files matched your search
@@ -34,6 +34,7 @@ from uuid import UUID
|
||||
|
||||
from gns3server.compute.project_manager import ProjectManager
|
||||
from gns3server.compute.project import Project
|
||||
from gns3server.compute.base_manager import BaseManager
|
||||
from gns3server.utils.path import is_safe_path
|
||||
from gns3server import schemas
|
||||
|
||||
@@ -131,6 +132,60 @@ async def delete_compute_project(project: Project = Depends(dep_project)) -> Non
|
||||
ProjectManager.instance().remove_project(project.id)
|
||||
|
||||
|
||||
async def _add_nio_binding(node, adapter_number, port_number, nio):
|
||||
"""
|
||||
Unified NIO-binding dispatch across node types. Each node type exposes a
|
||||
different method signature, so centralise the fan-out here for the batch
|
||||
endpoint. Dispatch keys off the manager class name (only dynamips/iou/qemu
|
||||
carry a ``_NODE_TYPE`` attribute, so it can't be used universally).
|
||||
"""
|
||||
|
||||
manager_name = type(node.manager).__name__
|
||||
# Adapter-based nodes: docker / qemu / vmware / virtualbox take
|
||||
# (adapter_number, nio); iou additionally takes port_number.
|
||||
if manager_name in ("Docker", "Qemu", "VMware", "VirtualBox"):
|
||||
await node.adapter_add_nio_binding(adapter_number, nio)
|
||||
elif manager_name == "IOU":
|
||||
await node.adapter_add_nio_binding(adapter_number, port_number, nio)
|
||||
elif manager_name == "VPCS":
|
||||
await node.port_add_nio_binding(port_number, nio)
|
||||
elif manager_name == "Builtin":
|
||||
# ethernet_switch / ethernet_hub / cloud / nat: add_nio(nio, port_number)
|
||||
await node.add_nio(nio, port_number)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Batch NIO creation not supported for node type '{manager_name}'",
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/projects/{project_id}/nios/batch",
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
)
|
||||
async def create_batch_nios(
|
||||
project_id: UUID,
|
||||
batch: schemas.BatchNIOCreate,
|
||||
project: Project = Depends(dep_project),
|
||||
) -> dict:
|
||||
"""
|
||||
Create many NIO bindings across nodes in a single request.
|
||||
|
||||
Used by the controller during project open to avoid one HTTP round-trip per
|
||||
NIO. Each entry resolves its node via the project, builds the NIO through
|
||||
the node's manager, and binds it. Nodes that are not started perform the
|
||||
binding in memory; started nodes additionally wire uBridge.
|
||||
"""
|
||||
|
||||
added = 0
|
||||
for entry in batch.nios:
|
||||
node = project.get_node(entry.node_id)
|
||||
nio = node.manager.create_nio(jsonable_encoder(entry.nio, exclude_unset=True))
|
||||
await _add_nio_binding(node, entry.adapter_number, entry.port_number, nio)
|
||||
added += 1
|
||||
return {"added": added}
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/files", response_model=List[schemas.ProjectFile])
|
||||
async def get_compute_project_files(project: Project = Depends(dep_project)) -> List[schemas.ProjectFile]:
|
||||
"""
|
||||
|
||||
Reference in new issue
Block a user