"""网络管理路由"""
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)}")