feat(managed-ne): add bulk import template download with device_type reference sheet

Expose GET /v1/managed-ne/import/template and a workbench button to download xlsx/csv templates listing supported Netmiko device types.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-28 09:41:25 +08:00
parent 042c015045
commit 3024e13812
6 changed files with 69 additions and 0 deletions

View file

@ -1,6 +1,7 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
from fastapi.responses import Response
from sqlalchemy.orm import Session
from .db import get_db
@ -9,6 +10,7 @@ from .ne_connect import schedule_connect_tests
from .ne_crypto import credentials_configured
from .ne_schemas import ConnectTestRequest, ManagedNeCreate, ManagedNeUpdate
from .ne_service import (
build_managed_ne_import_template,
create_managed_ne,
delete_managed_ne,
get_managed_ne,
@ -70,6 +72,16 @@ def api_delete_managed_ne(ne_id: str, db: Session = Depends(get_db)):
return delete_managed_ne(db, ne_id)
@router.get("/import/template")
def api_managed_ne_import_template(format: str = Query(default="xlsx")):
filename, payload, media_type = build_managed_ne_import_template(format)
return Response(
content=payload,
media_type=media_type,
headers={"content-disposition": f'attachment; filename="{filename}"'},
)
@router.post("/import")
async def api_import_managed_ne(file: UploadFile = File(...), db: Session = Depends(get_db)):
content = await file.read()

View file

@ -195,6 +195,51 @@ def delete_managed_ne(db: Session, ne_id: str) -> dict[str, bool]:
return {"ok": True}
def build_managed_ne_import_template(fmt: str = "xlsx") -> tuple[str, bytes, str]:
"""Return (filename, content, media_type) for bulk-import template."""
rows = [
{
"device_type": "cisco_ios",
"ip": "192.168.0.1",
"username": "admin",
"password": "your_password",
"port": 22,
"protocol": "ssh",
"name": "Core-SW1",
"vendor": "Cisco",
},
{
"device_type": "huawei",
"ip": "192.168.0.2",
"username": "admin",
"password": "your_password",
"port": 22,
"protocol": "ssh",
"name": "AGG-01",
"vendor": "Huawei",
},
]
df = pd.DataFrame(rows, columns=list(IMPORT_COLUMNS))
buf = BytesIO()
kind = str(fmt or "xlsx").strip().lower()
if kind == "csv":
df.to_csv(buf, index=False, encoding="utf-8-sig")
return (
"managed_ne_import_template.csv",
buf.getvalue(),
"text/csv; charset=utf-8",
)
device_types_df = pd.DataFrame({"device_type": list(SUPPORTED_DEVICE_TYPES)})
with pd.ExcelWriter(buf, engine="openpyxl") as writer:
df.to_excel(writer, sheet_name="import", index=False)
device_types_df.to_excel(writer, sheet_name="device_type", index=False)
return (
"managed_ne_import_template.xlsx",
buf.getvalue(),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)
def import_managed_ne(db: Session, content: bytes, filename: str) -> ImportResult:
_require_crypto()
name = str(filename or "").lower()