feat: KVM虚拟化管理平台初始版本
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
"""网络管理路由"""
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
from lxml import etree
|
||||
|
||||
from app.libvirt_conn import libvirt_conn
|
||||
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():
|
||||
"""列出所有网络"""
|
||||
conn = libvirt_conn.conn
|
||||
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范围
|
||||
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):
|
||||
"""获取网络详情"""
|
||||
conn = libvirt_conn.conn
|
||||
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):
|
||||
"""创建网络"""
|
||||
if net.mode == "bridge" and not net.bridge:
|
||||
raise HTTPException(status_code=400, detail="桥接模式必须指定桥接网卡")
|
||||
|
||||
if net.mode == "bridge":
|
||||
xml = f"""<network>
|
||||
<name>{net.name}</name>
|
||||
<forward mode='bridge'/>
|
||||
<bridge name='{net.bridge}'/>
|
||||
</network>"""
|
||||
else:
|
||||
# NAT或隔离模式
|
||||
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"""
|
||||
<dhcp>
|
||||
<range start='{start}' end='{end}'/>
|
||||
</dhcp>"""
|
||||
|
||||
forward_xml = f"<forward mode='{net.mode}'/>" if net.mode == "nat" else ""
|
||||
netmask = str(network.netmask)
|
||||
|
||||
xml = f"""<network>
|
||||
<name>{net.name}</name>
|
||||
{forward_xml}
|
||||
<bridge name='virbr-{net.name[:8]}' stp='on' delay='0'/>
|
||||
<ip address='{gateway}' netmask='{netmask}'>{dhcp_xml}
|
||||
</ip>
|
||||
</network>"""
|
||||
|
||||
with libvirt_conn.get_rw() 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):
|
||||
"""删除网络"""
|
||||
with libvirt_conn.get_rw() 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):
|
||||
"""网络操作: start/stop"""
|
||||
with libvirt_conn.get_rw() 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)}")
|
||||
Verwijs in nieuw issue
Block a user