feat: 多主机纳管、用户认证、noVNC控制台、深色主题
主要功能: - 多主机管理: 支持TCP/SSH方式纳管远程KVM主机 - 用户认证: JWT token认证, 默认admin/admin123 - noVNC控制台: 前端集成noVNC, WebSocket代理VNC连接 - 深色主题: 全局Element Plus深色主题覆盖 - 虚拟机操作: 克隆、迁移、XML编辑、快照管理 - 资源监控: CPU/内存/磁盘IO/网络流量实时监控 Bug修复: - libvirt getInfo()内存单位修正(MiB非KiB) - 远程主机VNC 0.0.0.0监听地址连接策略修复 - Dashboard定时器内存泄漏修复 - bcrypt版本兼容性修复
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"""主机管理路由"""
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from app.hosts import (
|
||||
list_hosts, get_host, add_host, remove_host,
|
||||
test_connection, update_host_status, HostCreate,
|
||||
)
|
||||
from app.libvirt_conn import conn_pool
|
||||
import time
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/list")
|
||||
async def api_list_hosts():
|
||||
"""列出所有已注册主机"""
|
||||
hosts = list_hosts()
|
||||
result = []
|
||||
for h in hosts:
|
||||
info = h.model_dump()
|
||||
# 尝试获取实时状态
|
||||
try:
|
||||
mgr = conn_pool.get(h.id)
|
||||
conn = mgr.conn
|
||||
alive = conn.isAlive()
|
||||
info["status"] = "online" if alive else "offline"
|
||||
if alive:
|
||||
host_data = conn.getInfo()
|
||||
info["cpu_cores"] = host_data[2]
|
||||
info["memory_mb"] = host_data[1] # getInfo()[1] 已经是 MiB 单位
|
||||
info["hostname"] = conn.getHostname()
|
||||
# 虚拟机数
|
||||
domains = conn.listAllDomains(0)
|
||||
info["vm_total"] = len(domains)
|
||||
info["vm_running"] = sum(1 for d in domains if d.isActive())
|
||||
update_host_status(h.id, info["status"])
|
||||
except Exception:
|
||||
info["status"] = "offline"
|
||||
update_host_status(h.id, "offline")
|
||||
result.append(info)
|
||||
return {"hosts": result, "total": len(result)}
|
||||
|
||||
|
||||
@router.get("/detail/{host_id}")
|
||||
async def api_get_host(host_id: str):
|
||||
"""获取单台主机详情"""
|
||||
host = get_host(host_id)
|
||||
if not host:
|
||||
raise HTTPException(status_code=404, detail=f"主机 '{host_id}' 不存在")
|
||||
info = host.model_dump()
|
||||
try:
|
||||
mgr = conn_pool.get(host_id)
|
||||
info["host_info"] = mgr.get_host_info()
|
||||
info["status"] = "online"
|
||||
# 虚拟机数
|
||||
domains = mgr.conn.listAllDomains(0)
|
||||
info["vm_total"] = len(domains)
|
||||
info["vm_running"] = sum(1 for d in domains if d.isActive())
|
||||
update_host_status(host_id, "online")
|
||||
except Exception as e:
|
||||
info["status"] = "offline"
|
||||
info["error"] = str(e)
|
||||
update_host_status(host_id, "offline")
|
||||
return info
|
||||
|
||||
|
||||
@router.post("/add")
|
||||
async def api_add_host(req: HostCreate):
|
||||
"""添加新主机"""
|
||||
# 先测试连接
|
||||
result = test_connection(req.uri)
|
||||
if not result["success"]:
|
||||
raise HTTPException(status_code=400, detail=f"连接测试失败: {result['error']}")
|
||||
|
||||
host = add_host(req)
|
||||
# 更新状态为在线
|
||||
update_host_status(host.id, "online")
|
||||
return {"message": f"主机 '{host.name}' 添加成功", "host": host.model_dump()}
|
||||
|
||||
|
||||
@router.delete("/delete/{host_id}")
|
||||
async def api_delete_host(host_id: str):
|
||||
"""删除主机"""
|
||||
if host_id == "local":
|
||||
raise HTTPException(status_code=400, detail="不能删除本机")
|
||||
if not remove_host(host_id):
|
||||
raise HTTPException(status_code=404, detail=f"主机 '{host_id}' 不存在")
|
||||
conn_pool.remove(host_id)
|
||||
return {"message": f"主机 '{host_id}' 已删除"}
|
||||
|
||||
|
||||
@router.post("/test")
|
||||
async def api_test_connection(req: HostCreate):
|
||||
"""测试连接(不添加主机)"""
|
||||
result = test_connection(req.uri)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/refresh/{host_id}")
|
||||
async def api_refresh_host(host_id: str):
|
||||
"""刷新主机状态"""
|
||||
host = get_host(host_id)
|
||||
if not host:
|
||||
raise HTTPException(status_code=404, detail=f"主机 '{host_id}' 不存在")
|
||||
try:
|
||||
# 重新建立连接
|
||||
conn_pool.remove(host_id)
|
||||
mgr = conn_pool.get(host_id)
|
||||
conn = mgr.conn
|
||||
alive = conn.isAlive()
|
||||
status = "online" if alive else "offline"
|
||||
update_host_status(host_id, status)
|
||||
return {"status": status, "hostname": conn.getHostname()}
|
||||
except Exception as e:
|
||||
update_host_status(host_id, "offline")
|
||||
conn_pool.remove(host_id)
|
||||
return {"status": "offline", "error": str(e)}
|
||||
Verwijs in nieuw issue
Block a user