feat: KVM虚拟化管理平台初始版本
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""快照管理路由"""
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
from lxml import etree
|
||||
|
||||
from app.libvirt_conn import libvirt_conn
|
||||
import libvirt
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class SnapshotCreate(BaseModel):
|
||||
name: str = Field(..., description="快照名称")
|
||||
description: Optional[str] = Field(None, description="快照描述")
|
||||
|
||||
|
||||
@router.get("/list/{vm_name}")
|
||||
async def list_snapshots(vm_name: str):
|
||||
"""列出虚拟机的所有快照"""
|
||||
conn = libvirt_conn.conn
|
||||
try:
|
||||
dom = conn.lookupByName(vm_name)
|
||||
except libvirt.libvirtError:
|
||||
raise HTTPException(status_code=404, detail=f"虚拟机 '{vm_name}' 不存在")
|
||||
|
||||
snapshots = []
|
||||
try:
|
||||
for snap in dom.listAllSnapshots(0):
|
||||
xml = etree.fromstring(snap.getXMLDesc().encode())
|
||||
desc = xml.find("description")
|
||||
state = xml.find("state")
|
||||
creation = xml.find("creationTime")
|
||||
|
||||
snapshots.append({
|
||||
"name": snap.getName(),
|
||||
"state": state.text if state is not None else "",
|
||||
"description": desc.text if desc is not None else "",
|
||||
"creation_time": int(creation.text) if creation is not None else 0,
|
||||
"is_current": snap.isCurrent() == 1,
|
||||
})
|
||||
except libvirt.libvirtError:
|
||||
pass # 没有快照
|
||||
|
||||
return {"vm": vm_name, "snapshots": snapshots, "total": len(snapshots)}
|
||||
|
||||
|
||||
@router.post("/create/{vm_name}")
|
||||
async def create_snapshot(vm_name: str, snap: SnapshotCreate):
|
||||
"""创建快照"""
|
||||
with libvirt_conn.get_rw() as rw_conn:
|
||||
try:
|
||||
dom = rw_conn.lookupByName(vm_name)
|
||||
except libvirt.libvirtError:
|
||||
raise HTTPException(status_code=404, detail=f"虚拟机 '{vm_name}' 不存在")
|
||||
|
||||
desc_xml = f"<description>{snap.description}</description>" if snap.description else ""
|
||||
xml = f"""<domainsnapshot>
|
||||
<name>{snap.name}</name>
|
||||
{desc_xml}
|
||||
</domainsnapshot>"""
|
||||
|
||||
try:
|
||||
dom.snapshotCreateXML(xml, 0)
|
||||
return {"message": f"快照 '{snap.name}' 创建成功"}
|
||||
except libvirt.libvirtError as e:
|
||||
raise HTTPException(status_code=500, detail=f"创建快照失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/revert/{vm_name}/{snap_name}")
|
||||
async def revert_snapshot(vm_name: str, snap_name: str):
|
||||
"""恢复快照"""
|
||||
with libvirt_conn.get_rw() as rw_conn:
|
||||
try:
|
||||
dom = rw_conn.lookupByName(vm_name)
|
||||
snap = dom.snapshotLookupByName(snap_name)
|
||||
except libvirt.libvirtError:
|
||||
raise HTTPException(status_code=404, detail="虚拟机或快照不存在")
|
||||
|
||||
try:
|
||||
dom.revertToSnapshot(snap)
|
||||
return {"message": f"已恢复到快照 '{snap_name}'"}
|
||||
except libvirt.libvirtError as e:
|
||||
raise HTTPException(status_code=500, detail=f"恢复快照失败: {str(e)}")
|
||||
|
||||
|
||||
@router.delete("/delete/{vm_name}/{snap_name}")
|
||||
async def delete_snapshot(vm_name: str, snap_name: str):
|
||||
"""删除快照"""
|
||||
with libvirt_conn.get_rw() as rw_conn:
|
||||
try:
|
||||
dom = rw_conn.lookupByName(vm_name)
|
||||
snap = dom.snapshotLookupByName(snap_name)
|
||||
except libvirt.libvirtError:
|
||||
raise HTTPException(status_code=404, detail="虚拟机或快照不存在")
|
||||
|
||||
try:
|
||||
snap.delete(0)
|
||||
return {"message": f"快照 '{snap_name}' 已删除"}
|
||||
except libvirt.libvirtError as e:
|
||||
raise HTTPException(status_code=500, detail=f"删除快照失败: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/detail/{vm_name}/{snap_name}")
|
||||
async def get_snapshot_detail(vm_name: str, snap_name: str):
|
||||
"""获取快照详情"""
|
||||
conn = libvirt_conn.conn
|
||||
try:
|
||||
dom = conn.lookupByName(vm_name)
|
||||
snap = dom.snapshotLookupByName(snap_name)
|
||||
except libvirt.libvirtError:
|
||||
raise HTTPException(status_code=404, detail="虚拟机或快照不存在")
|
||||
|
||||
return {"name": snap_name, "vm": vm_name, "xml": snap.getXMLDesc()}
|
||||
Reference in New Issue
Block a user