From 85e30301f5ec18bc9bffab245a8b54b1458c6f65 Mon Sep 17 00:00:00 2001 From: wangqing Date: Mon, 27 Oct 2025 17:08:00 +0800 Subject: [PATCH] fix build errors Signed-off-by: wangqing --- cmd/server/main.go | 9 ++-- internal/api/handlers/vm_handler.go | 41 +++++++++++++++++ internal/api/router.go | 2 +- internal/console/serial_proxy.go | 52 +++++++++++++++++++--- internal/domain/interfaces.go | 11 +++-- internal/libvirt/xmlbuilder/xml_builder.go | 26 +++++------ internal/service/vm_service.go | 2 +- 7 files changed, 113 insertions(+), 30 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 1765418..bc9969b 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -17,6 +17,7 @@ import ( "virt-manager-go/internal/api" "virt-manager-go/internal/config" + "virt-manager-go/internal/domain" "virt-manager-go/internal/service" ) @@ -46,15 +47,15 @@ func main() { vmService := service.NewVMService(connMgr, logger) // 自动连接本地 libvirt(可选) - if cfg.AutoConnect { + if cfg.Libvirt.AutoConnect { ctx := context.Background() - connID, err := connMgr.Connect(ctx, cfg.DefaultURI, &domain.ConnectionOptions{}) + connID, err := connMgr.Connect(ctx, cfg.Libvirt.DefaultURI, &domain.ConnectionOptions{}) if err != nil { logger.WithError(err).Error("Failed to auto-connect to libvirt") } else { logger.WithFields(logrus.Fields{ "connection_id": connID, - "uri": cfg.DefaultURI, + "uri": cfg.Libvirt.DefaultURI, }).Info("Auto-connected to libvirt") } } @@ -111,4 +112,4 @@ func setupLogger(level string) *logrus.Logger { logger.SetLevel(logLevel) return logger -} +} \ No newline at end of file diff --git a/internal/api/handlers/vm_handler.go b/internal/api/handlers/vm_handler.go index f43fb43..6dc64f6 100644 --- a/internal/api/handlers/vm_handler.go +++ b/internal/api/handlers/vm_handler.go @@ -164,6 +164,47 @@ func (h *VMHandler) RebootVM(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "VM rebooted"}) } + +// SuspendVM 暂停虚拟机 +// @Summary Suspend a virtual machine +// @Tags VM +// @Param connection_id path string true "Connection ID" +// @Param vm_name path string true "VM Name" +// @Success 200 {object} map[string]string +// @Router /api/v1/connections/{connection_id}/vms/{vm_name}/suspend [post] +func (h *VMHandler) SuspendVM(c *gin.Context) { + connID := c.Param("connection_id") + vmName := c.Param("vm_name") + if err := h.vmService.SuspendVM(c.Request.Context(), connID, vmName); err != nil { + h.logger.WithError(err).Error("Failed to suspend VM") + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "VM suspended successfully"}) +} + + +// ResumeVM 恢复虚拟机 +// @Summary Resume a virtual machine +// @Tags VM +// @Param connection_id path string true "Connection ID" +// @Param vm_name path string true "VM Name" +// @Success 200 {object} map[string]string +// @Router /api/v1/connections/{connection_id}/vms/{vm_name}/resume [post] +func (h *VMHandler) ResumeVM(c *gin.Context) { + connID := c.Param("connection_id") + vmName := c.Param("vm_name") + if err := h.vmService.ResumeVM(c.Request.Context(), connID, vmName); err != nil { + h.logger.WithError(err).Error("Failed to resume VM") + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "VM resumed successfully"}) +} + + // DeleteVM 删除虚拟机 // @Summary Delete a virtual machine // @Tags VM diff --git a/internal/api/router.go b/internal/api/router.go index 822c147..ab2b4cf 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -17,7 +17,7 @@ type Router struct { engine *gin.Engine vmHandler *handlers.VMHandler connHandler *handlers.ConnectionHandler - serialProxy *console.SerialProxy + serialProxy domain.ConsoleProxy logger *logrus.Logger } diff --git a/internal/console/serial_proxy.go b/internal/console/serial_proxy.go index fef6df8..00ce326 100644 --- a/internal/console/serial_proxy.go +++ b/internal/console/serial_proxy.go @@ -92,7 +92,7 @@ func (p *serialProxy) StartSerialConsole(ctx context.Context, connID, vmName str devName := "serial0" flags := libvirt.DOMAIN_CONSOLE_FORCE - if err := dom.OpenConsole(devName, stream, uint32(flags)); err != nil { + if err := dom.OpenConsole(devName, stream, libvirt.DomainConsoleFlags(flags)); err != nil { stream.Free() return nil, fmt.Errorf("failed to open console: %w", err) } @@ -344,7 +344,7 @@ func NewVNCProxy(connMgr domain.ConnectionManager, logger *logrus.Logger) *vncPr } // StartVNCProxy 启动 VNC 代理 -func (p *vncProxy) StartVNCProxy(ctx context.Context, connID, vmName string) (domain.ProxyInfo, error) { +func (p *serialProxy) StartVNCProxy(ctx context.Context, connID, vmName string) (domain.ProxyInfo, error) { conn, err := p.connMgr.GetConnection(connID) if err != nil { return domain.ProxyInfo{}, err @@ -356,16 +356,18 @@ func (p *vncProxy) StartVNCProxy(ctx context.Context, connID, vmName string) (do } defer dom.Free() - // 获取 VM 的 XML 配置 + // 获取虚拟机的XML配置 xmlData, err := dom.GetXMLDesc(0) if err != nil { - return domain.ProxyInfo{}, fmt.Errorf("failed to get VM XML: %w", err) + return domain.ProxyInfo{}, fmt.Errorf("failed to get domain XML: %w", err) } + // 解析XML配置以获取设备信息 // 解析 XML 获取 VNC 端口和监听地址 // 这里需要使用 libvirtxml 解析 - // 为简化示例,返回基础信息 + // 已声明 xmlData 变量并赋值,现在添加注释表明其用途 + _ = xmlData // 显式忽略变量以避免编译错误,后续实现会使用该变量 proxyInfo := domain.ProxyInfo{ ProxyURL: fmt.Sprintf("vnc://localhost:5900"), @@ -382,6 +384,46 @@ func (p *vncProxy) StartVNCProxy(ctx context.Context, connID, vmName string) (do return proxyInfo, nil } +// StartSPICEProxy 启动 SPICE 代理 +func (p *serialProxy) StartSPICEProxy(ctx context.Context, connID, vmName string) (domain.ProxyInfo, error) { + conn, err := p.connMgr.GetConnection(connID) + if err != nil { + return domain.ProxyInfo{}, err + } + + dom, err := conn.LookupDomainByName(vmName) + if err != nil { + return domain.ProxyInfo{}, fmt.Errorf("VM not found: %w", err) + } + defer dom.Free() + + // 获取虚拟机的XML配置 + xmlData, err := dom.GetXMLDesc(0) + if err != nil { + return domain.ProxyInfo{}, fmt.Errorf("failed to get domain XML: %w", err) + } + + // 解析XML配置以获取设备信息 + // 解析 XML 获取 SPICE 端口和监听地址 + // 这里需要使用 libvirtxml 解析 + // 为简化示例,返回基础信息 + _ = xmlData // 显式忽略变量以避免编译错误,后续实现会使用该变量 + + proxyInfo := domain.ProxyInfo{ + ProxyURL: fmt.Sprintf("spice://localhost:5900"), + WSPath: fmt.Sprintf("/api/v1/console/spice/%s/%s", connID, vmName), + Token: generateToken(), + ExpiresAt: time.Now().Add(1 * time.Hour), + } + + p.logger.WithFields(logrus.Fields{ + "vm": vmName, + "proxy_url": proxyInfo.ProxyURL, + }).Info("SPICE proxy info generated") + + return proxyInfo, nil +} + func generateToken() string { return fmt.Sprintf("token-%d", time.Now().UnixNano()) } diff --git a/internal/domain/interfaces.go b/internal/domain/interfaces.go index 803e248..b30acf2 100644 --- a/internal/domain/interfaces.go +++ b/internal/domain/interfaces.go @@ -4,6 +4,7 @@ package domain import ( "context" "io" + "net/http" "time" "libvirt.org/go/libvirt" @@ -58,7 +59,7 @@ type ConsoleProxy interface { StartSPICEProxy(ctx context.Context, connID, vmName string) (ProxyInfo, error) // WebSocket 代理 - HandleWebSocket(connID, vmName, consoleType string) error + HandleWebSocket(w http.ResponseWriter, r *http.Request, connID, vmName string) error } // XMLBuilder XML 构建器接口 @@ -66,9 +67,11 @@ type XMLBuilder interface { BuildVMXML(config *VMCreateConfig) (string, error) BuildDiskXML(disk *DiskConfig) (string, error) BuildNetworkXML(net *NetworkConfig) (string, error) - ParseVMXML(xmlData string) (*VMConfig, error) + ParseVMXML(xmlData string) (*VMCreateConfig, error) + CloneVMXML(srcXML, newName string) (string, error) } + // 领域模型 // VMInfo 虚拟机信息 @@ -189,8 +192,8 @@ type ConnectionInfo struct { Type string `json:"type"` // qemu, xen, lxc Hostname string `json:"hostname"` Connected bool `json:"connected"` - Version uint64 `json:"version"` - LibVersion uint64 `json:"lib_version"` + Version uint32 `json:"version"` + LibVersion uint32 `json:"lib_version"` ConnectedAt time.Time `json:"connected_at"` } diff --git a/internal/libvirt/xmlbuilder/xml_builder.go b/internal/libvirt/xmlbuilder/xml_builder.go index 79c8926..6cfdb3f 100644 --- a/internal/libvirt/xmlbuilder/xml_builder.go +++ b/internal/libvirt/xmlbuilder/xml_builder.go @@ -319,14 +319,13 @@ func (b *xmlBuilder) addGraphics(domainConfig *libvirtxml.Domain, gfxCfg *domain // addConsole 添加串行控制台 func (b *xmlBuilder) addConsole(domainConfig *libvirtxml.Domain) { + port := uint(0) console := libvirtxml.DomainConsole{ - Type: "pty", Target: &libvirtxml.DomainConsoleTarget{ Type: "serial", - Port: new(uint), + Port: &port, }, } - *console.Target.Port = 0 domainConfig.Devices.Consoles = append(domainConfig.Devices.Consoles, console) } @@ -336,12 +335,10 @@ func (b *xmlBuilder) addVideo(domainConfig *libvirtxml.Domain) { video := libvirtxml.DomainVideo{ Model: libvirtxml.DomainVideoModel{ Type: "qxl", - RAM: new(uint), - VRam: new(uint), + Ram: uint(65536), + VRam: uint(65536), }, } - *video.Model.RAM = 65536 - *video.Model.VRam = 65536 domainConfig.Devices.Videos = append(domainConfig.Devices.Videos, video) } @@ -420,24 +417,23 @@ func (b *xmlBuilder) BuildNetworkXML(net *domain.NetworkConfig) (string, error) } // ParseVMXML 解析虚拟机 XML -func (b *xmlBuilder) ParseVMXML(xmlData string) (*domain.VMConfig, error) { - domainConfig := &libvirtxml.Domain{} +func (b *xmlBuilder) ParseVMXML(xmlData string) (*domain.VMCreateConfig, error) { + var domainConfig libvirtxml.Domain if err := domainConfig.Unmarshal(xmlData); err != nil { - return nil, fmt.Errorf("failed to unmarshal domain XML: %w", err) + return nil, err } - config := &domain.VMConfig{ + config := &domain.VMCreateConfig{ Metadata: make(map[string]string), } if domainConfig.VCPU != nil { - vcpus := domainConfig.VCPU.Value - config.CPUs = &vcpus + config.CPUs = uint(domainConfig.VCPU.Value) } if domainConfig.Memory != nil { - mem := domainConfig.Memory.Value / 1024 // KB to MB - config.Memory = &mem + mem := uint64(domainConfig.Memory.Value / 1024) // KB to MB + config.Memory = mem } if domainConfig.OS != nil && domainConfig.OS.Type != nil { diff --git a/internal/service/vm_service.go b/internal/service/vm_service.go index f4bdc57..05acb69 100644 --- a/internal/service/vm_service.go +++ b/internal/service/vm_service.go @@ -352,7 +352,7 @@ func (s *vmService) UpdateVMConfig(ctx context.Context, connID, vmName string, c // 更新 CPU(需要关机) if config.CPUs != nil && !isRunning { - if err := dom.SetVcpusFlags(*config.CPUs, libvirt.DOMAIN_AFFECT_CONFIG); err != nil { + if err := dom.SetVcpusFlags(*config.CPUs, libvirt.DOMAIN_VCPU_CONFIG); err != nil { return fmt.Errorf("failed to update CPUs: %w", err) } } -- Gitee