// Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package qemu import ( "fmt" "strings" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/hostman/guestman/desc" ) type Monitor struct { Id string Port uint Mode string } func generatePCIDeviceOption(dev *desc.PCIDevice) string { cmd := fmt.Sprintf( "-device %s,id=%s,%s", dev.DevType, dev.Id, dev.OptionsStr(), ) return cmd } func chardevOption(c *desc.CharDev) string { cmd := fmt.Sprintf("-chardev %s,id=%s", c.Backend, c.Id) if c.Name != "" { cmd += fmt.Sprintf(",name=%s", c.Name) } cmd += desc.OptionsToString(c.Options) return cmd } func virtSerialPortOption(p *desc.VirtSerialPort, bus string) string { cmd := fmt.Sprintf( "-device virtserialport,bus=%s.0,chardev=%s,name=%s", bus, p.Chardev, p.Name, ) cmd += desc.OptionsToString(p.Options) return cmd } func usbControllerOption(usb *desc.UsbController) string { cmd := generatePCIDeviceOption(usb.PCIDevice) if usb.MasterBus != nil { cmd += fmt.Sprintf( ",masterbus=%s.0,firstport=%d", usb.MasterBus.Masterbus, usb.MasterBus.Port, ) } return cmd } func usbRedirOptions(usbredir *desc.UsbRedirctDesc) []string { opts := make([]string, 0) opts = append(opts, usbControllerOption(usbredir.EHCI1)) opts = append(opts, usbControllerOption(usbredir.UHCI1)) opts = append(opts, usbControllerOption(usbredir.UHCI2)) opts = append(opts, usbControllerOption(usbredir.UHCI3)) opts = append(opts, chardevOption(usbredir.UsbRedirDev1.Source)) opts = append(opts, fmt.Sprintf("-device usb-redir,chardev=%s,id=%s", usbredir.UsbRedirDev1.Source.Id, usbredir.UsbRedirDev1.Id)) opts = append(opts, chardevOption(usbredir.UsbRedirDev2.Source)) opts = append(opts, fmt.Sprintf("-device usb-redir,chardev=%s,id=%s", usbredir.UsbRedirDev2.Source.Id, usbredir.UsbRedirDev2.Id)) return opts } func generateSpiceOptions(port uint, spice *desc.SSpiceDesc) []string { opts := make([]string, 0) // spice spiceCmd := fmt.Sprintf("-spice port=%d", port) spiceCmd += desc.OptionsToString(spice.Options) opts = append(opts, spiceCmd) // intel-hda and codec hda-duplex opts = append(opts, generatePCIDeviceOption(spice.IntelHDA.PCIDevice)) codec := spice.IntelHDA.Codec opts = append(opts, fmt.Sprintf("-device %s,id=%s,bus=%s.0,cad=%d", codec.Type, codec.Id, spice.IntelHDA.Id, codec.Cad), ) // serial port opts = append(opts, generatePCIDeviceOption(spice.VdagentSerial.PCIDevice)) opts = append(opts, chardevOption(spice.Vdagent)) opts = append(opts, virtSerialPortOption(spice.VdagentSerialPort, spice.VdagentSerial.Id)) // usb redirct opts = append(opts, usbRedirOptions(spice.UsbRedirct)...) return opts } func generatePciControllerOptions(controllers []*desc.PCIController) []string { opts := make([]string, 0) for i := 0; i < len(controllers); i++ { switch controllers[i].CType { case desc.CONTROLLER_TYPE_PCIE_TO_PCI_BRIDGE: opts = append(opts, fmt.Sprintf( "-device pcie-pci-bridge,id=pci.%d,bus=pcie.%d,addr=0x%02x", i, controllers[i].Bus, controllers[i].Slot, )) case desc.CONTROLLER_TYPE_PCI_BRIDGE: opts = append(opts, fmt.Sprintf( "-device pci-bridge,id=pci.%d,bus=pci.%d,chassis_nr=%d,addr=0x%02x", i, controllers[i].Bus, i, controllers[i].Slot), ) case desc.CONTROLLER_TYPE_PCIE_ROOT_PORT: opts = append(opts, fmt.Sprintf( "-device ioh3420,id=pci.%d,chassis=%d,bus=pcie.%d,addr=0x%02x", i, i, controllers[i].Bus, controllers[i].Slot, ), ) } } return opts } func generateNumaOption(memId string, nodeId *uint16, cpus *string) string { cmd := fmt.Sprintf("-numa node,memdev=%s", memId) if nodeId != nil { cmd += fmt.Sprintf(",nodeid=%d", *nodeId) } if cpus != nil { cpuSegs := strings.Split(*cpus, ",") for _, cpuSeg := range cpuSegs { cmd += fmt.Sprintf(",cpus=%s", cpuSeg) } } return cmd } func generateMemObjectWithNumaOptions(mem *desc.SMemDesc) string { cmds := []string{} cmds = append(cmds, generateObjectOption(mem.Object)) cmds = append(cmds, generateNumaOption(mem.Id, mem.NodeId, mem.Cpus)) return strings.Join(cmds, " ") } func generateMemoryOption(memDesc *desc.SGuestMem) string { cmds := []string{} cmds = append(cmds, fmt.Sprintf( "-m %dM,slots=%d,maxmem=%dM", memDesc.SizeMB, memDesc.Slots, memDesc.MaxMem, )) if memDesc.Mem != nil { cmds = append(cmds, generateMemObjectWithNumaOptions(&memDesc.Mem.SMemDesc)) for i := range memDesc.Mem.Mems { cmds = append(cmds, generateMemObjectWithNumaOptions(&memDesc.Mem.Mems[i])) } } for i := 0; i < len(memDesc.MemSlots); i++ { memDev := memDesc.MemSlots[i].MemDev memObj := memDesc.MemSlots[i].MemObj cmds = append(cmds, generateObjectOption(memObj.Object)) cmds = append(cmds, fmt.Sprintf("-device %s,id=%s,memdev=%s", memDev.Type, memDev.Id, memObj.Id)) } return strings.Join(cmds, " ") } func generateMachineOption(drvOpt QemuOptions, desc *desc.SGuestDesc) string { cmd := fmt.Sprintf("-machine %s,accel=%s", desc.Machine, desc.MachineDesc.Accel) if desc.MachineDesc.GicVersion != nil { cmd += fmt.Sprintf(",gic-version=%s", *desc.MachineDesc.GicVersion) } if desc.NoHpet != nil && *desc.NoHpet { machineOpts, noHpetCmd := drvOpt.NoHpet() if machineOpts { cmd += fmt.Sprintf(",%s", noHpetCmd) } else if noHpetCmd != "" { cmd += fmt.Sprintf(" %s", noHpetCmd) } } return cmd } func generateSMPOption(guestDesc *desc.SGuestDesc) string { cpu := guestDesc.CpuDesc startCpus := cpu.Cpus if guestDesc.MemDesc.Mem != nil { if len(guestDesc.MemDesc.Mem.Mems) > 0 { startCpus = 1 } } if cpu.MaxCpus%2 > 0 { return fmt.Sprintf( "-smp cpus=%d,maxcpus=%d", startCpus, cpu.MaxCpus, ) } else { return fmt.Sprintf( "-smp cpus=%d,sockets=%d,cores=%d,maxcpus=%d", startCpus, cpu.Sockets, cpu.Cores, cpu.MaxCpus, ) } } func generateCPUOption(cpu *desc.SGuestCpu) string { cmd := fmt.Sprintf("-cpu %s", cpu.Model) for feat, enable := range cpu.Features { if enable { cmd += "," + feat + "=on" } else { cmd += "," + feat + "=off" } } if len(cpu.Vendor) > 0 { cmd += fmt.Sprintf(",vendor=%s", cpu.Vendor) } if len(cpu.Level) > 0 { cmd += fmt.Sprintf(",level=%s", cpu.Level) } return cmd } func getMonitorOptions(drvOpt QemuOptions, input *Monitor) []string { if input == nil { return nil } idDev := input.Id + "dev" opts := []string{ drvOpt.MonitorChardev(idDev, input.Port, "127.0.0.1"), drvOpt.Mon(idDev, input.Id, input.Mode), } return opts } func generateScsiOptions(scsi *desc.SGuestVirtioScsi) string { opt := generatePCIDeviceOption(scsi.PCIDevice) if scsi.NumQueues != nil && *scsi.NumQueues > 0 { opt += fmt.Sprintf(",num_queues=%d,vectors=%d", *scsi.NumQueues, *scsi.NumQueues+1) } return opt } func generateInitrdOptions(drvOpt QemuOptions, initrdPath, kernel string) []string { opts := make([]string, 0) opts = append(opts, drvOpt.Initrd(initrdPath)) opts = append(opts, drvOpt.Kernel(kernel)) opts = append(opts, "-append yn_rescue_mode=true") return opts } func generateKickstartBootOptions(drvOpt QemuOptions, kickstartBoot *KickstartBootInfo) []string { opts := make([]string, 0) opts = append(opts, drvOpt.Kernel(kickstartBoot.KernelPath)) opts = append(opts, drvOpt.Initrd(kickstartBoot.InitrdPath)) if kickstartBoot.KernelArgs != "" { // due to blank space in kickstart args, '' is needed opts = append(opts, fmt.Sprintf("-append '%s'", kickstartBoot.KernelArgs)) } return opts } func generateDisksOptions(drvOpt QemuOptions, disks []*desc.SGuestDisk, isEncrypt, isMaster bool, osName string) []string { opts := make([]string, 0) for _, disk := range disks { if disk.Driver == api.DISK_DRIVER_VFIO { continue } if isMaster { opts = append(opts, getMasterDiskDriveOption(drvOpt, disk, isEncrypt)) } else { opts = append(opts, getDiskDriveOption(drvOpt, disk, isEncrypt)) } opts = append(opts, getDiskDeviceOption(drvOpt, disk, osName)) } return opts } func getMasterDiskDriveOption(drvOpt QemuOptions, disk *desc.SGuestDisk, isEncrypt bool) string { format := disk.Format diskIndex := disk.Index cacheMode := disk.CacheMode aioMode := disk.AioMode opt := "if=none,driver=quorum,read-pattern=fifo,is-backup-mode=on,vote-threshold=1" opt += fmt.Sprintf(",id=drive_%d", diskIndex) opt += fmt.Sprintf(",cache=%s", cacheMode) if isLocalStorage(disk) { opt += fmt.Sprintf(",aio=%s", aioMode) } opt += fmt.Sprintf(",children.0.file.filename=$DISK_%d", diskIndex) if format == "raw" { opt += ",children.0.file.format=raw" } return drvOpt.Drive(opt) } func getDiskDriveOption(drvOpt QemuOptions, disk *desc.SGuestDisk, isEncrypt bool) string { format := disk.Format diskIndex := disk.Index cacheMode := disk.CacheMode aioMode := disk.AioMode var opt string opt = fmt.Sprintf("file=$DISK_%d", diskIndex) opt += ",if=none" opt += fmt.Sprintf(",id=drive_%d", diskIndex) if len(format) == 0 || format == "qcow2" { // pass # qemu will automatically detect image format } else if format == "raw" { opt += ",format=raw" } opt += fmt.Sprintf(",cache=%s", cacheMode) if isLocalStorage(disk) { opt += fmt.Sprintf(",aio=%s", aioMode) } if len(disk.Url) > 0 { // # a remote file backed image opt += ",copy-on-read=on" } if isLocalStorage(disk) { opt += ",file.locking=off" } if isEncrypt { opt += ",encrypt.format=luks,encrypt.key-secret=sec0" } if disk.AutoReset { opt += ",snapshot=on" } // #opt += ",media=disk" return drvOpt.Drive(opt) } func isLocalStorage(disk *desc.SGuestDisk) bool { if disk.StorageType == api.STORAGE_LOCAL || disk.StorageType == api.STORAGE_LVM || len(disk.StorageType) == 0 { return true } else { return false } } func getDiskDeviceOption(optDrv QemuOptions, disk *desc.SGuestDisk, osName string) string { diskIndex := disk.Index diskDriver := disk.Driver numQueues := disk.NumQueues isSsd := disk.IsSSD var opt = "" opt += GetDiskDeviceModel(diskDriver) if osName != OS_NAME_VMWARE { serial := strings.ReplaceAll(disk.DiskId, "-", "") opt += fmt.Sprintf(",serial=%s", serial) opt += optDrv.ScsiDeviceId(serial, diskDriver) } opt += fmt.Sprintf(",drive=drive_%d", diskIndex) if diskDriver == DISK_DRIVER_VIRTIO { // virtio-blk if disk.Pci != nil { opt += fmt.Sprintf(",bus=%s,addr=%s", disk.Pci.BusStr(), disk.Pci.SlotFunc()) } // opt += fmt.Sprintf(",num-queues=%d,vectors=%d,iothread=iothread0", numQueues, numQueues+1) opt += ",iothread=iothread0" if numQueues > 0 { opt += fmt.Sprintf(",num-queues=%d,vectors=%d", numQueues, numQueues+1) } } else if utils.IsInStringArray(diskDriver, []string{DISK_DRIVER_SCSI, DISK_DRIVER_PVSCSI}) { opt += ",bus=scsi.0" } else if diskDriver == DISK_DRIVER_IDE { opt += fmt.Sprintf(",bus=ide.%d,unit=%d", diskIndex/2, diskIndex%2) } else if diskDriver == DISK_DRIVER_SATA { opt += fmt.Sprintf(",bus=ahci0.%d", diskIndex) } opt += fmt.Sprintf(",id=drive_%d", diskIndex) if isSsd { if diskDriver == DISK_DRIVER_SCSI { opt += ",rotation_rate=1" } } if disk.BootIndex != nil && *disk.BootIndex >= 0 { opt += fmt.Sprintf(",bootindex=%d", *disk.BootIndex) } return optDrv.Device(opt) } func generateCdromOptions(optDrv QemuOptions, cdroms []*desc.SGuestCdrom) []string { opts := make([]string, 0) for _, cdrom := range cdroms { //cdromDriveId := cdrom driveOpt := fmt.Sprintf("id=%s", cdrom.Id) driveOpt += desc.OptionsToString(cdrom.DriveOptions) var cdromPath = cdrom.Path if len(cdromPath) > 0 { driveOpt += fmt.Sprintf(",file=%s", cdromPath) } if cdrom.Ide != nil { opts = append(opts, optDrv.Drive(driveOpt)) devOpt := fmt.Sprintf("%s,drive=%s,bus=ide.1", cdrom.Ide.DevType, cdrom.Id) if len(cdromPath) > 0 { if cdrom.BootIndex != nil && *cdrom.BootIndex >= 0 { devOpt += fmt.Sprintf(",bootindex=%d", *cdrom.BootIndex) } } // TODO: ,bus=ide.%d,unit=%d //, cdrom.Ide.Bus, cdrom.Ide.Unit) opts = append(opts, optDrv.Device(devOpt)) } else if cdrom.Scsi != nil { if len(cdromPath) > 0 { opts = append(opts, optDrv.Drive(driveOpt)) devOpt := fmt.Sprintf("%s,drive=%s,id=%s", cdrom.Scsi.DevType, cdrom.Id, cdrom.Scsi.Id) devOpt += desc.OptionsToString(cdrom.Scsi.Options) if len(cdromPath) > 0 { if cdrom.BootIndex != nil && *cdrom.BootIndex >= 0 { devOpt += fmt.Sprintf(",bootindex=%d", *cdrom.BootIndex) } } opts = append(opts, optDrv.Device(devOpt)) } } } return opts } func generateFloppyOptions(optDrv QemuOptions, floppys []*desc.SGuestFloppy) []string { opts := make([]string, 0) for _, floppy := range floppys { driveOpt := fmt.Sprintf("id=%s", floppy.Id) driveOpt += desc.OptionsToString(floppy.DriveOptions) var floppyPath = floppy.Path if len(floppyPath) > 0 { driveOpt += fmt.Sprintf(",file=%s", floppyPath) } if floppy.Floppy != nil { opts = append(opts, optDrv.Drive(driveOpt)) devOpt := fmt.Sprintf("%s,drive=%s", floppy.Floppy.DevType, floppy.Id) // TODO: ,bus=ide.%d,unit=%d //, cdrom.Ide.Bus, cdrom.Ide.Unit) opts = append(opts, optDrv.Device(devOpt)) } } return opts } func GetDiskDeviceModel(driver string) string { if driver == DISK_DRIVER_VIRTIO { return "virtio-blk-pci" } else if utils.IsInStringArray(driver, []string{DISK_DRIVER_SCSI, DISK_DRIVER_PVSCSI}) { return "scsi-hd" } else if driver == DISK_DRIVER_IDE { return "ide-hd" } else if driver == DISK_DRIVER_SATA { return "ide-hd" } else { return "None" } } func generateNicOptions(drvOpt QemuOptions, input *GenerateStartOptionsInput) ([]string, error) { opts := make([]string, 0) nics := input.GuestDesc.Nics //input.guest for idx := range nics { if nics[idx].Driver == api.NETWORK_DRIVER_VFIO { continue } var nicTrafficExceed = false if input.NicTraffics != nil { nicTraffic, ok := input.NicTraffics[nics[idx].Mac] if ok && nicTraffic != nil { if nics[idx].TxTrafficLimit > 0 && nicTraffic.TxTraffic > nics[idx].TxTrafficLimit { nicTrafficExceed = true } if nics[idx].RxTrafficLimit > 0 && nicTraffic.RxTraffic > nics[idx].RxTrafficLimit { nicTrafficExceed = true } } } netDevOpt, err := getNicNetdevOption(drvOpt, nics[idx], input.IsKVMSupport, nicTrafficExceed) if err != nil { return nil, errors.Wrapf(err, "getNicNetdevOption %v", nics[idx]) } opts = append(opts, netDevOpt, // aarch64 with addr lead to: // virtio_net: probe of virtioN failed with error -22 getNicDeviceOption(drvOpt, nics[idx], input)) } return opts, nil } func getNicNetdevOption(drvOpt QemuOptions, nic *desc.SGuestNetwork, isKVMSupport bool, nicTrafficExceed bool) (string, error) { if nic.Ifname == "" { return "", errors.Error("ifname is empty") } if nic.UpscriptPath == "" { return "", errors.Error("upscript_path is empty") } if nic.DownscriptPath == "" { return "", errors.Error("downscript_path is empty") } opt := "-netdev type=tap" opt += fmt.Sprintf(",id=%s", nic.Ifname) opt += fmt.Sprintf(",ifname=%s", nic.Ifname) if nic.Driver == "virtio" && isKVMSupport { opt += ",vhost=on,vhostforce=off" if nic.NumQueues > 1 { opt += fmt.Sprintf(",queues=%d", nic.NumQueues) } } if !nicTrafficExceed { opt += fmt.Sprintf(",script=%s", nic.UpscriptPath) } opt += fmt.Sprintf(",downscript=%s", nic.DownscriptPath) return opt, nil } func getNicDeviceOption( drvOpt QemuOptions, nic *desc.SGuestNetwork, input *GenerateStartOptionsInput, ) string { cmd := generatePCIDeviceOption(nic.Pci) cmd += fmt.Sprintf(",netdev=%s", nic.Ifname) cmd += fmt.Sprintf(",mac=%s", nic.Mac) if nic.Driver == "virtio" { if nic.NumQueues > 1 { cmd += ",mq=on" } if nic.Vectors != nil { cmd += fmt.Sprintf(",vectors=%d", *nic.Vectors) } cmd += fmt.Sprintf("$(nic_speed %d)", nic.Bw) if nic.Bridge == input.OVNIntegrationBridge { cmd += fmt.Sprintf("$(nic_mtu %q)", nic.Bridge) } } return cmd } func GetNicDeviceModel(name string) string { if name == "virtio" { return "virtio-net-pci" } else if name == "e1000" { return "e1000-82545em" } else { return name } } func generateUsbDeviceOption(usbControllerId string, usb *desc.UsbDevice) string { cmd := fmt.Sprintf("-device %s,bus=%s.0,id=%s", usb.DevType, usbControllerId, usb.Id) cmd += desc.OptionsToString(usb.Options) return cmd } func generateVfioDeviceOption(dev *desc.SGuestIsolatedDevice) []string { opts := make([]string, 0) for i := 0; i < len(dev.VfioDevs); i++ { cmd := generatePCIDeviceOption(dev.VfioDevs[i].PCIDevice) if dev.MdevId != "" { cmd += fmt.Sprintf(",sysfsdev=/sys/bus/mdev/devices/%s", dev.MdevId) } else { cmd += fmt.Sprintf(",host=%s", dev.VfioDevs[i].HostAddr) if dev.VfioDevs[i].XVga { cmd += ",x-vga=on" } } opts = append(opts, cmd) } return opts } func generateIsolatedDeviceOptions(guestDesc *desc.SGuestDesc) []string { opts := make([]string, 0) for i := 0; i < len(guestDesc.IsolatedDevices); i++ { if guestDesc.IsolatedDevices[i].Usb != nil { opts = append(opts, generateUsbDeviceOption(guestDesc.Usb.Id, guestDesc.IsolatedDevices[i].Usb), ) //} else if guestDesc.IsolatedDevices[i].DevType { } else if len(guestDesc.IsolatedDevices[i].VfioDevs) > 0 { opts = append(opts, generateVfioDeviceOption(guestDesc.IsolatedDevices[i])..., ) } } return opts } func generateObjectOption(o *desc.Object) string { cmd := fmt.Sprintf("-object %s,id=%s", o.ObjType, o.Id) cmd += desc.OptionsToString(o.Options) return cmd } func getRNGRandomOptions(rng *desc.SGuestRng) []string { cmd := generatePCIDeviceOption(rng.PCIDevice) cmd += fmt.Sprintf(",rng=%s", rng.RngRandom.Id) return []string{ generateObjectOption(rng.RngRandom), cmd, } } func generateQgaOptions(guestDesc *desc.SGuestDesc) []string { opts := make([]string, 0) opts = append(opts, chardevOption(guestDesc.Qga.Socket)) opts = append(opts, virtSerialPortOption(guestDesc.Qga.SerialPort, guestDesc.VirtioSerial.Id)) return opts } func generateISASerialOptions(isaSerial *desc.SGuestIsaSerial) []string { opts := make([]string, 0) opts = append(opts, chardevOption(isaSerial.Pty)) opts = append(opts, fmt.Sprintf("-device isa-serial,chardev=%s,id=%s", isaSerial.Pty.Id, isaSerial.Id)) return opts } func generateKickstartSerialOptions(kickstartBoot *KickstartBootInfo) []string { if kickstartBoot == nil || kickstartBoot.SerialFilePath == "" { return nil } opts := make([]string, 0) chardevId := "kickstart_serial" // Create chardev with file backend chardevOpt := fmt.Sprintf("-chardev file,path=%s,id=%s", kickstartBoot.SerialFilePath, chardevId) opts = append(opts, chardevOpt) // Create ISA serial device serialOpt := fmt.Sprintf("-device isa-serial,chardev=%s,id=kickstart_serial_device", chardevId) opts = append(opts, serialOpt) return opts } func generatePvpanicDeviceOption(pvpanic *desc.SGuestPvpanic) string { return fmt.Sprintf("-device pvpanic,id=%s,ioport=0x%x", pvpanic.Id, pvpanic.Ioport) } func generateTpmDevOptions(tpm *desc.SGuestTpm) []string { opts := make([]string, 0) opts = append(opts, chardevOption(tpm.TpmSock)) opts = append(opts, fmt.Sprintf("-tpmdev emulator,id=%s,chardev=%s", tpm.Id, tpm.TpmSock.Id)) opts = append(opts, fmt.Sprintf("-device tpm-tis,tpmdev=%s", tpm.Id)) return opts } func getMigrateOptions(drvOpt QemuOptions, input *GenerateStartOptionsInput) []string { opts := make([]string, 0) if input.NeedMigrate { if input.LiveMigrateUseTLS { opts = append(opts, "-incoming defer") } else { opts = append(opts, fmt.Sprintf("-incoming tcp:[::]:%d", input.LiveMigratePort)) } } else if input.GuestDesc.IsSlave { opts = append(opts, fmt.Sprintf("-incoming tcp:[::]:%d", input.LiveMigratePort)) } return opts } type GenerateStartOptionsInput struct { QemuVersion Version QemuArch Arch GuestDesc *desc.SGuestDesc IsKVMSupport bool NicTraffics map[string]*api.SNicTrafficRecord EnableUUID bool OsName string HugepagesEnabled bool EnableMemfd bool EnableTpm bool OVNIntegrationBridge string Devices []string OVMFPath string OVMFVarsPath string VNCPort uint VNCPassword bool EnableLog bool HMPMonitor *Monitor QMPMonitor *Monitor IsVdiSpice bool SpicePort uint PidFilePath string HomeDir string ExtraOptions []string EnableRNGRandom bool EnableSerialDevice bool NeedMigrate bool LiveMigratePort uint LiveMigrateUseTLS bool EnablePvpanic bool EncryptKeyPath string RescueInitrdPath string // rescue initramfs path RescueKernelPath string // rescue kernel path KickstartBoot *KickstartBootInfo } type KickstartBootInfo struct { Config *api.KickstartConfig MountPath string KernelPath string InitrdPath string KernelArgs string SerialFilePath string ConfigIsoPath string } func (input *GenerateStartOptionsInput) HasBootIndex() bool { for _, cdrom := range input.GuestDesc.Cdroms { if cdrom.BootIndex != nil && *cdrom.BootIndex >= 0 { return true } } for _, disk := range input.GuestDesc.Disks { if disk.BootIndex != nil && *disk.BootIndex >= 0 { return true } } return false } func GenerateStartOptions( input *GenerateStartOptionsInput, ) (string, error) { drv, ok := GetCommand(input.QemuVersion, input.QemuArch) if !ok { return "", errors.Errorf("Qemu comand driver %s %s not registered", input.QemuVersion, input.QemuArch) } drvOpt := drv.GetOptions() opts := make([]string, 0) // generate cpu options cpuOpt := generateCPUOption(input.GuestDesc.CpuDesc) opts = append(opts, drvOpt.FreezeCPU(), cpuOpt) if input.EnableLog { opts = append(opts, drvOpt.Log(input.EnableLog)) } // TODO hmp - - opts = append(opts, getMonitorOptions(drvOpt, input.HMPMonitor)...) if input.QMPMonitor != nil { opts = append(opts, getMonitorOptions(drvOpt, input.QMPMonitor)...) } opts = append(opts, drvOpt.RTC(), // drvOpt.Daemonize(), drvOpt.Nodefaults(), drvOpt.Nodefconfig(), // drvOpt.NoKVMPitReinjection(), drvOpt.Global(), generateMachineOption(drvOpt, input.GuestDesc), drvOpt.KeyboardLayoutLanguage("en-us"), generateSMPOption(input.GuestDesc), drvOpt.Name(input.GuestDesc.Name), drvOpt.UUID(input.EnableUUID, input.GuestDesc.Uuid), generateMemoryOption(input.GuestDesc.MemDesc), ) // bootOrder enableMenu := false for _, cdrom := range input.GuestDesc.Cdroms { if cdrom.Path != "" { enableMenu = true } } // Note that it does not make sense to use the bootindex property together // with the "-boot order=..." (or "-boot once=...") parameter. var bootOrder *string if !input.HasBootIndex() { bootOrder = &input.GuestDesc.BootOrder } opts = append(opts, drvOpt.Boot(bootOrder, enableMenu)) // bios if input.GuestDesc.Bios == BIOS_UEFI { if input.OVMFPath == "" { return "", errors.Errorf("input OVMF path is empty") } fmOpt, err := drvOpt.BIOS(input.OVMFPath, input.OVMFVarsPath, input.HomeDir) if err != nil { return "", errors.Wrap(err, "bios option") } opts = append(opts, fmOpt) } if input.OsName == OS_NAME_MACOS { opts = append(opts, drvOpt.Device("isa-applesmc,osk=ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc")) } if input.GuestDesc.Vga != "none" && input.GuestDesc.VgaDevice != nil { opts = append(opts, generatePCIDeviceOption(input.GuestDesc.VgaDevice.PCIDevice)) } // vdi spice if input.IsVdiSpice && input.GuestDesc.VdiDevice != nil && input.GuestDesc.VdiDevice.Spice != nil { opts = append(opts, generateSpiceOptions(input.SpicePort, input.GuestDesc.VdiDevice.Spice)...) } else { opts = append(opts, drvOpt.VNC(input.VNCPort, input.VNCPassword)) } // iothread object opts = append(opts, drvOpt.Object("iothread", map[string]string{"id": "iothread0"})) isEncrypt := false if len(input.EncryptKeyPath) > 0 { opts = append(opts, drvOpt.Object("secret", map[string]string{"id": "sec0", "file": input.EncryptKeyPath, "format": "base64"})) isEncrypt = true } if input.GuestDesc.VirtioSerial != nil { opts = append(opts, generatePCIDeviceOption(input.GuestDesc.VirtioSerial.PCIDevice)) } opts = append(opts, generatePciControllerOptions(input.GuestDesc.PCIControllers)...) if input.GuestDesc.VirtioScsi != nil { opts = append(opts, generateScsiOptions(input.GuestDesc.VirtioScsi)) } else if input.GuestDesc.PvScsi != nil { opts = append(opts, generatePCIDeviceOption(input.GuestDesc.PvScsi.PCIDevice)) } if input.GuestDesc.SataController != nil { opts = append(opts, generatePCIDeviceOption(input.GuestDesc.SataController.PCIDevice)) } // generate initrd and kernel options if input.GuestDesc.LightMode { opts = append(opts, generateInitrdOptions( drvOpt, input.RescueInitrdPath, input.RescueKernelPath, )...) } else if input.KickstartBoot != nil { opts = append(opts, generateKickstartBootOptions( drvOpt, input.KickstartBoot, )...) } // generate disk options opts = append(opts, generateDisksOptions( drvOpt, input.GuestDesc.Disks, isEncrypt, input.GuestDesc.IsMaster, input.OsName)...) // cdrom opts = append(opts, generateCdromOptions(drvOpt, input.GuestDesc.Cdroms)...) //floppy opts = append(opts, generateFloppyOptions(drvOpt, input.GuestDesc.Floppys)...) // generate nics nicOpts, err := generateNicOptions(drvOpt, input) if err != nil { return "", errors.Wrap(err, "generateNicOptions") } opts = append(opts, nicOpts...) if !input.GuestDesc.LightMode { if !input.QemuArch.IsX86() { if input.GuestDesc.Usb != nil { opts = append(opts, generatePCIDeviceOption(input.GuestDesc.Usb.PCIDevice)) for _, device := range input.Devices { opts = append(opts, drvOpt.Device(device)) } } } else { opts = append(opts, drvOpt.USB()) for _, device := range input.Devices { opts = append(opts, drvOpt.Device(device)) } if input.GuestDesc.Usb != nil { opts = append(opts, generatePCIDeviceOption(input.GuestDesc.Usb.PCIDevice)) } } } // isolated devices if len(input.GuestDesc.IsolatedDevices) > 0 && !input.GuestDesc.LightMode { opts = append(opts, generateIsolatedDeviceOptions(input.GuestDesc)...) } // pidfile opts = append(opts, drvOpt.Pidfile(input.PidFilePath)) // qga // opts = append(opts, drvOpt.QGA(input.HomeDir)...) if input.GuestDesc.Qga != nil { opts = append(opts, generateQgaOptions(input.GuestDesc)...) } // random device if input.GuestDesc.Rng != nil { opts = append(opts, getRNGRandomOptions(input.GuestDesc.Rng)...) } // serial device if input.GuestDesc.IsaSerial != nil { opts = append(opts, generateISASerialOptions(input.GuestDesc.IsaSerial)...) } // kickstart serial device if input.KickstartBoot != nil { opts = append(opts, generateKickstartSerialOptions(input.KickstartBoot)...) } // migrate options opts = append(opts, getMigrateOptions(drvOpt, input)...) // pvpanic device if input.GuestDesc.Pvpanic != nil { opts = append(opts, generatePvpanicDeviceOption(input.GuestDesc.Pvpanic)) } if input.GuestDesc.Tpm != nil { opts = append(opts, generateTpmDevOptions(input.GuestDesc.Tpm)...) } // move extra options to end of cmdline if len(input.ExtraOptions) != 0 { opts = append(opts, input.ExtraOptions...) } return strings.Join(opts, " "), nil }