功能:完善云管助手与GPU故障恢复核对
This commit is contained in:
1 parent
b6026f9759
commit
9f7b01e4db
42 files changed
+3576
-120
No files matched your search
@@ -0,0 +1,121 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"vdi-cloud/platform/internal/model"
|
||||
)
|
||||
|
||||
type desktopReadinessCheck struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type desktopReadiness struct {
|
||||
DesktopID string `json:"desktopId"`
|
||||
ObservedAt time.Time `json:"observedAt"`
|
||||
Ready bool `json:"ready"`
|
||||
PowerState string `json:"powerState"`
|
||||
ServiceState string `json:"serviceState"`
|
||||
SetupRequired bool `json:"setupRequired"`
|
||||
Checks []desktopReadinessCheck `json:"checks"`
|
||||
}
|
||||
|
||||
// checkDesktopReadiness verifies the parts of the provisioning contract that
|
||||
// the platform can observe without logging into the guest OS. It never starts
|
||||
// or stops a VM and never changes guest configuration.
|
||||
func (s *Server) checkDesktopReadiness(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
var desktop model.Desktop
|
||||
if err := s.DB.GetContext(ctx, &desktop, "SELECT * FROM desktops WHERE id=$1", c.Param("id")); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
s.fail(c, 404, "desktop_not_found", "桌面不存在")
|
||||
} else {
|
||||
s.dbError(c, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
result := desktopReadiness{
|
||||
DesktopID: desktop.ID, ObservedAt: time.Now().UTC(), Ready: false,
|
||||
PowerState: desktop.PowerState, ServiceState: desktop.ServiceState,
|
||||
SetupRequired: desktop.SetupRequired, Checks: []desktopReadinessCheck{},
|
||||
}
|
||||
add := func(name, status, message string) {
|
||||
result.Checks = append(result.Checks, desktopReadinessCheck{Name: name, Status: status, Message: message})
|
||||
}
|
||||
|
||||
if desktop.Provider != "proxmox" {
|
||||
add("provider", "fail", "桌面不是 Proxmox 资源,无法执行真实连接探测")
|
||||
c.JSON(200, result)
|
||||
return
|
||||
}
|
||||
if desktop.Endpoint == "" {
|
||||
add("endpoint", "fail", "尚未配置桌面 IP 或主机名")
|
||||
} else {
|
||||
add("endpoint", "pass", "已配置桌面连接地址")
|
||||
}
|
||||
if desktop.SetupRequired {
|
||||
add("initialization", "fail", "尚未确认系统身份、硬件编码、Sunshine 和终端配对")
|
||||
} else {
|
||||
add("initialization", "pass", "已完成平台初始化确认")
|
||||
}
|
||||
|
||||
pve := s.currentPVE()
|
||||
if pve == nil {
|
||||
add("power", "unknown", "尚未配置 Proxmox,无法读取真实电源状态")
|
||||
} else {
|
||||
power, err := pve.Power(ctx, desktop.Node, desktop.VMID)
|
||||
if err != nil {
|
||||
result.PowerState = "unknown"
|
||||
add("power", "unknown", "无法读取 Proxmox 实际电源状态")
|
||||
} else {
|
||||
result.PowerState = power
|
||||
if power == "running" {
|
||||
add("power", "pass", "虚拟机正在运行")
|
||||
} else {
|
||||
add("power", "fail", "虚拟机未运行,请先开机")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serviceReady := false
|
||||
if desktop.Endpoint != "" && result.PowerState == "running" {
|
||||
serviceReady = sunshineReady(ctx, desktop.Endpoint)
|
||||
if serviceReady {
|
||||
result.ServiceState = "ready"
|
||||
add("sunshine", "pass", "Sunshine serverinfo 已响应")
|
||||
} else {
|
||||
result.ServiceState = "unknown"
|
||||
add("sunshine", "fail", "Sunshine 未响应,请检查服务、网络和硬件编码配置")
|
||||
}
|
||||
} else {
|
||||
add("sunshine", "unknown", "虚拟机未运行或尚未配置连接地址,暂不探测 Sunshine")
|
||||
}
|
||||
|
||||
result.Ready = !desktop.SetupRequired && result.PowerState == "running" && serviceReady
|
||||
if _, err := s.DB.ExecContext(ctx, "UPDATE desktops SET power_state=$1,service_state=$2,updated_at=now() WHERE id=$3", result.PowerState, result.ServiceState, desktop.ID); err != nil {
|
||||
s.dbError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(200, result)
|
||||
}
|
||||
|
||||
// sunshineReadyAt is split out so the real probe remains fixed to Sunshine's
|
||||
// serverinfo port while tests can use an isolated local listener.
|
||||
func sunshineReadyAt(ctx context.Context, endpoint, port string) bool {
|
||||
host := endpoint
|
||||
if parsed, _, err := net.SplitHostPort(endpoint); err == nil {
|
||||
host = parsed
|
||||
}
|
||||
if strings.ContainsAny(host, "/\\@?#") || host == "" {
|
||||
return false
|
||||
}
|
||||
return sunshineHTTPProbe(ctx, host, port)
|
||||
}
|
||||
Reference in new issue
Block a user