feat: Add batch link_ids to link_delete/link_reset, fields filter to link_list

This commit is contained in:
YueGuobin 2026-06-14 22:43:44 +08:00
parent d6c362b3f0
commit be69670333
No known key found for this signature in database
2 changed files with 53 additions and 12 deletions

View File

@ -612,10 +612,16 @@ async def link_create(
@mcp.tool()
async def link_delete(
project_id: Annotated[str, Field(description="UUID of the project")],
link_id: Annotated[str, Field(description="UUID of the link to delete")],
link_id: Annotated[str | None, Field(description="Link UUID (single mode)")] = None,
link_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — delete multiple links in parallel")] = None,
) -> list[dict[str, Any]]:
"""Delete a link from a project."""
return await asyncio.to_thread(_run_handler_sync, delete_link_handler, {"project_id": project_id, "link_id": link_id})
"""Delete one or more links from a project."""
params = {"project_id": project_id}
if link_ids:
params["link_ids"] = link_ids
else:
params["link_id"] = link_id
return await asyncio.to_thread(_run_handler_sync, delete_link_handler, params)
@mcp.tool()
@ -909,9 +915,10 @@ async def node_links(
@mcp.tool()
async def link_reset(
project_id: Annotated[str, Field(description="UUID of the project")],
link_id: Annotated[str, Field(description="UUID of the link")],
link_id: Annotated[str | None, Field(description="Link UUID (single mode)")] = None,
link_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — reset multiple links in parallel")] = None,
) -> list[dict[str, Any]]:
"""Reset a link by tearing down the underlying UDP connection and recreating it.
"""Reset one or more links by tearing down and recreating the UDP connection.
Use cases:
- Clear accumulated packet errors/drops from the link's UDP connection
@ -920,9 +927,12 @@ async def link_reset(
Filters are preserved but their internal application state resets.
"""
return await asyncio.to_thread(_run_handler_sync, reset_link_handler, {
"project_id": project_id, "link_id": link_id,
})
params = {"project_id": project_id}
if link_ids:
params["link_ids"] = link_ids
else:
params["link_id"] = link_id
return await asyncio.to_thread(_run_handler_sync, reset_link_handler, params)
@mcp.tool()

View File

@ -137,9 +137,24 @@ def create_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dic
def delete_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
project_id = params.get("project_id")
if not project_id:
return {"error": "project_id is required"}
link_ids = params.get("link_ids")
if link_ids:
if not isinstance(link_ids, list):
return {"error": "link_ids must be a list"}
conn = _get_connector(gns3_ctx)
def _del(lid):
try:
conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/links/{lid}")
return {"link_id": lid, "status": "deleted"}
except Exception as e:
return {"link_id": lid, "status": "error", "error": str(e)}
with ThreadPoolExecutor(max_workers=min(len(link_ids), BATCH_MAX_WORKERS)) as pool:
return list(pool.map(_del, link_ids))
link_id = params.get("link_id")
if not project_id or not link_id:
return {"error": "project_id and link_id are required"}
if not link_id:
return {"error": "link_id or link_ids is required"}
conn = _get_connector(gns3_ctx)
conn.http_call("delete", f"{conn.base_url}/projects/{project_id}/links/{link_id}")
return {"message": f"Link {link_id} deleted", "link_id": link_id}
@ -167,9 +182,25 @@ def update_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dic
def reset_link_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
project_id = params.get("project_id")
if not project_id:
return {"error": "project_id is required"}
link_ids = params.get("link_ids")
if link_ids:
if not isinstance(link_ids, list):
return {"error": "link_ids must be a list"}
conn = _get_connector(gns3_ctx)
def _rst(lid):
try:
url = f"{conn.base_url}/projects/{project_id}/links/{lid}/reset"
r = conn.http_call("post", url).json()
return {"link_id": lid, "status": "reset", "link": r}
except Exception as e:
return {"link_id": lid, "status": "error", "error": str(e)}
with ThreadPoolExecutor(max_workers=min(len(link_ids), BATCH_MAX_WORKERS)) as pool:
return list(pool.map(_rst, link_ids))
link_id = params.get("link_id")
if not project_id or not link_id:
return {"error": "project_id and link_id are required"}
if not link_id:
return {"error": "link_id or link_ids is required"}
conn = _get_connector(gns3_ctx)
url = f"{conn.base_url}/projects/{project_id}/links/{link_id}/reset"
result = conn.http_call("post", url).json()