"""网络管理路由""" from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel, Field from typing import Optional, List from lxml import etree from app.libvirt_conn import conn_pool import libvirt router = APIRouter() class NetworkCreate(BaseModel): name: str = Field(..., description="网络名称") mode: str = Field("nat", description="模式: nat/bridge/isolated") subnet: str = Field("192.168.100.0/24", description="子网") bridge: Optional[str] = Field(None, description="桥接网卡名(mode=bridge时必填)") dhcp_start: Optional[str] = Field(None, description="DHCP起始IP") dhcp_end: Optional[str] = Field(None, description="DHCP结束IP") @router.get("/list") async def list_networks(host_id: str = Query("local")): """列出所有网络""" conn = conn_pool.get_conn(host_id) networks = conn.listAllNetworks(0) result = [] for net in networks: xml = etree.fromstring(net.XMLDesc(0).encode()) forward = xml.find("forward") mode = forward.get("mode", "isolated") if forward is not None else "isolated" ip_elem = xml.find("ip") address = ip_elem.get("address", "") if ip_elem is not None else "" netmask = ip_elem.get("netmask", "") if ip_elem is not None else "" bridge = xml.find("bridge") bridge_name = bridge.get("name", "") if bridge is not None else "" dhcp_range = None dhcp = xml.find(".//dhcp") if dhcp is not None: r = dhcp.find("range") if r is not None: dhcp_range = {"start": r.get("start", ""), "end": r.get("end", "")} leases = [] try: for lease in net.DHCPLeases(): leases.append({ "ip": lease.get("ipaddr", ""), "mac": lease.get("mac", ""), "hostname": lease.get("hostname", ""), "expiry": lease.get("expirytime", 0), }) except Exception: pass result.append({ "name": net.name(), "active": net.isActive() == 1, "persistent": net.isPersistent() == 1, "autostart": net.autostart() == 1, "mode": mode, "address": address, "netmask": netmask, "bridge": bridge_name, "dhcp": dhcp_range, "leases": leases, }) return {"networks": result, "total": len(result)} @router.get("/detail/{name}") async def get_network(name: str, host_id: str = Query("local")): """获取网络详情""" conn = conn_pool.get_conn(host_id) try: net = conn.networkLookupByName(name) except libvirt.libvirtError: raise HTTPException(status_code=404, detail=f"网络 '{name}' 不存在") xml_str = net.XMLDesc(0) return {"name": name, "xml": xml_str, "active": net.isActive() == 1} @router.post("/create") async def create_network(net: NetworkCreate, host_id: str = Query("local")): """创建网络""" if net.mode == "bridge" and not net.bridge: raise HTTPException(status_code=400, detail="桥接模式必须指定桥接网卡") if net.mode == "bridge": xml = f""" {net.name} """ else: import ipaddress network = ipaddress.ip_network(net.subnet, strict=False) gateway = str(network.network_address + 1) dhcp_xml = "" if net.mode == "nat": start = net.dhcp_start or str(network.network_address + 2) end = net.dhcp_end or str(network.network_address + 254) dhcp_xml = f""" """ forward_xml = f"" if net.mode == "nat" else "" netmask = str(network.netmask) xml = f""" {net.name} {forward_xml} {dhcp_xml} """ with conn_pool.get_rw(host_id) as rw_conn: try: n = rw_conn.networkDefineXML(xml) n.setAutostart(1) n.create() return {"message": f"网络 '{net.name}' 创建成功"} except libvirt.libvirtError as e: raise HTTPException(status_code=500, detail=f"创建网络失败: {str(e)}") @router.delete("/delete/{name}") async def delete_network(name: str, host_id: str = Query("local")): """删除网络""" with conn_pool.get_rw(host_id) as rw_conn: try: net = rw_conn.networkLookupByName(name) except libvirt.libvirtError: raise HTTPException(status_code=404, detail=f"网络 '{name}' 不存在") if net.isActive(): net.destroy() net.undefine() return {"message": f"网络 '{name}' 已删除"} @router.post("/action/{name}") async def network_action(name: str, action: str, host_id: str = Query("local")): """网络操作: start/stop""" with conn_pool.get_rw(host_id) as rw_conn: try: net = rw_conn.networkLookupByName(name) except libvirt.libvirtError: raise HTTPException(status_code=404, detail=f"网络 '{name}' 不存在") try: if action == "start": net.create() elif action == "stop": net.destroy() else: raise HTTPException(status_code=400, detail=f"不支持的操作: {action}") return {"message": f"网络 '{name}' {action} 成功"} except libvirt.libvirtError as e: raise HTTPException(status_code=500, detail=f"操作失败: {str(e)}")