From 1f75b12f89a94a616d0fc9990211d5a101f40b47 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Sun, 5 Nov 2023 06:52:21 +0800 Subject: [PATCH] feature: initial spice streaming agent support. Support not fully mature yet. TODO: 1. upgrade host spice protocol to >=0.14 2. handle live migration device changes --- pkg/hostman/guestman/desc/desc.go | 7 ++++ pkg/hostman/guestman/pci.go | 45 ++++++++++++++++++-------- pkg/hostman/guestman/pci_test.go | 2 +- pkg/hostman/guestman/qemu-kvm.go | 7 ++++ pkg/hostman/guestman/qemu-kvmhelper.go | 4 +-- pkg/hostman/guestman/qemu/generate.go | 6 ++++ 6 files changed, 54 insertions(+), 17 deletions(-) diff --git a/pkg/hostman/guestman/desc/desc.go b/pkg/hostman/guestman/desc/desc.go index 13f02fa4b42..1d2161ba842 100644 --- a/pkg/hostman/guestman/desc/desc.go +++ b/pkg/hostman/guestman/desc/desc.go @@ -95,6 +95,8 @@ type SGuestHardwareDesc struct { // vnc or spice Vdi string VdiDevice *SGuestVdi `json:",omitempty"` + // vdi options + VdiOptions map[string]string VirtioScsi *SGuestVirtioScsi `json:",omitempty"` PvScsi *SGuestPvScsi `json:",omitempty"` @@ -245,6 +247,11 @@ type SSpiceDesc struct { Vdagent *CharDev VdagentSerialPort *VirtSerialPort + // https://gitlab.freedesktop.org/spice/spice-streaming-agent + // Spice Streaming Agent + StreamingAgent *CharDev + StreamingAgentPort *VirtSerialPort + // usb redirect UsbRedirct *UsbRedirctDesc diff --git a/pkg/hostman/guestman/pci.go b/pkg/hostman/guestman/pci.go index c17a376f8b9..262dad6a321 100644 --- a/pkg/hostman/guestman/pci.go +++ b/pkg/hostman/guestman/pci.go @@ -22,6 +22,7 @@ import ( "yunion.io/x/log" "yunion.io/x/pkg/errors" + compute_api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/hostman/guestman/desc" "yunion.io/x/onecloud/pkg/hostman/guestman/qemu" "yunion.io/x/onecloud/pkg/hostman/monitor" @@ -78,7 +79,7 @@ func (s *SKVMGuestInstance) initGuestDevicesDesc(pciRoot, pciBridge *desc.PCICon // vdi device for spice s.Desc.VdiDevice = new(desc.SGuestVdi) if s.IsVdiSpice() { - s.initSpiceDevices(pciRoot) + s.initSpiceDevices(pciRoot, s.isEnableSpiceStreaming()) } s.initVirtioSerial(pciRoot) @@ -494,7 +495,7 @@ func (s *SKVMGuestInstance) initGuestVga(pciRoot *desc.PCIController) { } } -func (s *SKVMGuestInstance) initSpiceDevices(pciRoot *desc.PCIController) { +func (s *SKVMGuestInstance) initSpiceDevices(pciRoot *desc.PCIController, enableStreaming bool) { spice := new(desc.SSpiceDesc) spice.IntelHDA = &desc.SoundCard{ PCIDevice: desc.NewPCIDevice(pciRoot.CType, "intel-hda", "sound0"), @@ -535,6 +536,22 @@ func (s *SKVMGuestInstance) initSpiceDevices(pciRoot *desc.PCIController) { "nr": "1", }, } + + if enableStreaming { + // -chardev spiceport,name=org.spice-space.stream.0,id=charchannel1 + streamingAgentCharDevId := "charchannel1" + streamingAgentCharDevName := "org.spice-space.stream.0" + spice.StreamingAgent = desc.NewCharDev("spiceport", streamingAgentCharDevId, streamingAgentCharDevName) + // -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel1,id=channel1,name=org.spice-space.stream.0 + spice.StreamingAgentPort = &desc.VirtSerialPort{ + Chardev: streamingAgentCharDevId, + Name: streamingAgentCharDevName, + Options: map[string]string{ + "nr": "2", + }, + } + } + spice.Options = map[string]string{ "disable-ticketing": "off", "seamless-migration": "on", @@ -869,8 +886,8 @@ func (s *SKVMGuestInstance) initGuestDescFromExistingGuest( } case "sound0": if s.Desc.VdiDevice.Spice == nil { - s.Desc.Vdi = "spice" - s.initSpiceDevices(pciRoot) + s.Desc.Vdi = compute_api.VM_VDI_PROTOCOL_SPICE + s.initSpiceDevices(pciRoot, false) } s.Desc.VdiDevice.Spice.IntelHDA.PCIAddr = pciAddr err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.IntelHDA.PCIDevice, -1, nil) @@ -879,8 +896,8 @@ func (s *SKVMGuestInstance) initGuestDescFromExistingGuest( } case "vdagent-serial0": if s.Desc.VdiDevice.Spice == nil { - s.Desc.Vdi = "spice" - s.initSpiceDevices(pciRoot) + s.Desc.Vdi = compute_api.VM_VDI_PROTOCOL_SPICE + s.initSpiceDevices(pciRoot, false) } s.Desc.VdiDevice.Spice.VdagentSerial.PCIAddr = pciAddr err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.VdagentSerial.PCIDevice, -1, nil) @@ -899,8 +916,8 @@ func (s *SKVMGuestInstance) initGuestDescFromExistingGuest( } case "usbspice": if s.Desc.VdiDevice.Spice == nil { - s.Desc.Vdi = "spice" - s.initSpiceDevices(pciRoot) + s.Desc.Vdi = compute_api.VM_VDI_PROTOCOL_SPICE + s.initSpiceDevices(pciRoot, false) } s.Desc.VdiDevice.Spice.UsbRedirct.EHCI1.PCIAddr = pciAddr multiFunc := true @@ -910,8 +927,8 @@ func (s *SKVMGuestInstance) initGuestDescFromExistingGuest( } case "uhci1": if s.Desc.VdiDevice.Spice == nil { - s.Desc.Vdi = "spice" - s.initSpiceDevices(pciRoot) + s.Desc.Vdi = compute_api.VM_VDI_PROTOCOL_SPICE + s.initSpiceDevices(pciRoot, false) } multiFunc := true s.Desc.VdiDevice.Spice.UsbRedirct.UHCI1.PCIAddr = pciAddr @@ -921,8 +938,8 @@ func (s *SKVMGuestInstance) initGuestDescFromExistingGuest( } case "uhci2": if s.Desc.VdiDevice.Spice == nil { - s.Desc.Vdi = "spice" - s.initSpiceDevices(pciRoot) + s.Desc.Vdi = compute_api.VM_VDI_PROTOCOL_SPICE + s.initSpiceDevices(pciRoot, false) } multiFunc := true s.Desc.VdiDevice.Spice.UsbRedirct.UHCI2.PCIAddr = pciAddr @@ -932,8 +949,8 @@ func (s *SKVMGuestInstance) initGuestDescFromExistingGuest( } case "uhci3": if s.Desc.VdiDevice.Spice == nil { - s.Desc.Vdi = "spice" - s.initSpiceDevices(pciRoot) + s.Desc.Vdi = compute_api.VM_VDI_PROTOCOL_SPICE + s.initSpiceDevices(pciRoot, false) } multiFunc := true s.Desc.VdiDevice.Spice.UsbRedirct.UHCI3.PCIAddr = pciAddr diff --git a/pkg/hostman/guestman/pci_test.go b/pkg/hostman/guestman/pci_test.go index 4155b80a6eb..f25e8852a22 100644 --- a/pkg/hostman/guestman/pci_test.go +++ b/pkg/hostman/guestman/pci_test.go @@ -170,7 +170,7 @@ func TestSKVMGuestInstance_initGuestDesc(t *testing.T) { // vdi device for spice s.Desc.VdiDevice = new(desc.SGuestVdi) if s.IsVdiSpice() { - s.initSpiceDevices(pciRoot) + s.initSpiceDevices(pciRoot, s.isEnableSpiceStreaming()) } s.initVirtioSerial(pciRoot) diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index 6421d79215f..7776858bd12 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -3275,3 +3275,10 @@ func (s *SKVMGuestInstance) getTftpEndpoint(baremetalManagerUri string) (string, return endpoint, nil } + +func (s *SKVMGuestInstance) isEnableSpiceStreaming() bool { + if s.Desc.Vdi == api.VM_VDI_PROTOCOL_SPICE && s.Desc.VdiOptions != nil && s.Desc.VdiOptions["streaming"] == "true" { + return true + } + return false +} diff --git a/pkg/hostman/guestman/qemu-kvmhelper.go b/pkg/hostman/guestman/qemu-kvmhelper.go index 4701124aeb0..945fe1400c1 100644 --- a/pkg/hostman/guestman/qemu-kvmhelper.go +++ b/pkg/hostman/guestman/qemu-kvmhelper.go @@ -154,7 +154,7 @@ func (s *SKVMGuestInstance) CpuMax() (uint, error) { } func (s *SKVMGuestInstance) IsVdiSpice() bool { - return s.Desc.Vdi == "spice" + return s.Desc.Vdi == api.VM_VDI_PROTOCOL_SPICE } func (s *SKVMGuestInstance) GetOsName() string { @@ -254,7 +254,7 @@ func (s *SKVMGuestInstance) isPcie() bool { func (s *SKVMGuestInstance) GetVdiProtocol() string { vdi := s.Desc.Vdi if vdi == "" { - vdi = "vnc" + vdi = api.VM_VDI_PROTOCOL_VNC } return vdi } diff --git a/pkg/hostman/guestman/qemu/generate.go b/pkg/hostman/guestman/qemu/generate.go index f294d54e545..fc8d216abd4 100644 --- a/pkg/hostman/guestman/qemu/generate.go +++ b/pkg/hostman/guestman/qemu/generate.go @@ -103,6 +103,12 @@ func generateSpiceOptions(port uint, spice *desc.SSpiceDesc) []string { opts = append(opts, chardevOption(spice.Vdagent)) opts = append(opts, virtSerialPortOption(spice.VdagentSerialPort, spice.VdagentSerial.Id)) + if spice.StreamingAgent != nil { + // streaming port + opts = append(opts, chardevOption(spice.StreamingAgent)) + opts = append(opts, virtSerialPortOption(spice.StreamingAgentPort, spice.VdagentSerial.Id)) + } + // usb redirct opts = append(opts, usbRedirOptions(spice.UsbRedirct)...) return opts