generate.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package qemu
  15. import (
  16. "fmt"
  17. "strings"
  18. "yunion.io/x/pkg/errors"
  19. "yunion.io/x/pkg/utils"
  20. api "yunion.io/x/onecloud/pkg/apis/compute"
  21. "yunion.io/x/onecloud/pkg/hostman/guestman/desc"
  22. )
  23. type Monitor struct {
  24. Id string
  25. Port uint
  26. Mode string
  27. }
  28. func generatePCIDeviceOption(dev *desc.PCIDevice) string {
  29. cmd := fmt.Sprintf(
  30. "-device %s,id=%s,%s", dev.DevType, dev.Id, dev.OptionsStr(),
  31. )
  32. return cmd
  33. }
  34. func chardevOption(c *desc.CharDev) string {
  35. cmd := fmt.Sprintf("-chardev %s,id=%s", c.Backend, c.Id)
  36. if c.Name != "" {
  37. cmd += fmt.Sprintf(",name=%s", c.Name)
  38. }
  39. cmd += desc.OptionsToString(c.Options)
  40. return cmd
  41. }
  42. func virtSerialPortOption(p *desc.VirtSerialPort, bus string) string {
  43. cmd := fmt.Sprintf(
  44. "-device virtserialport,bus=%s.0,chardev=%s,name=%s",
  45. bus, p.Chardev, p.Name,
  46. )
  47. cmd += desc.OptionsToString(p.Options)
  48. return cmd
  49. }
  50. func usbControllerOption(usb *desc.UsbController) string {
  51. cmd := generatePCIDeviceOption(usb.PCIDevice)
  52. if usb.MasterBus != nil {
  53. cmd += fmt.Sprintf(
  54. ",masterbus=%s.0,firstport=%d",
  55. usb.MasterBus.Masterbus, usb.MasterBus.Port,
  56. )
  57. }
  58. return cmd
  59. }
  60. func usbRedirOptions(usbredir *desc.UsbRedirctDesc) []string {
  61. opts := make([]string, 0)
  62. opts = append(opts, usbControllerOption(usbredir.EHCI1))
  63. opts = append(opts, usbControllerOption(usbredir.UHCI1))
  64. opts = append(opts, usbControllerOption(usbredir.UHCI2))
  65. opts = append(opts, usbControllerOption(usbredir.UHCI3))
  66. opts = append(opts, chardevOption(usbredir.UsbRedirDev1.Source))
  67. opts = append(opts, fmt.Sprintf("-device usb-redir,chardev=%s,id=%s", usbredir.UsbRedirDev1.Source.Id, usbredir.UsbRedirDev1.Id))
  68. opts = append(opts, chardevOption(usbredir.UsbRedirDev2.Source))
  69. opts = append(opts, fmt.Sprintf("-device usb-redir,chardev=%s,id=%s", usbredir.UsbRedirDev2.Source.Id, usbredir.UsbRedirDev2.Id))
  70. return opts
  71. }
  72. func generateSpiceOptions(port uint, spice *desc.SSpiceDesc) []string {
  73. opts := make([]string, 0)
  74. // spice
  75. spiceCmd := fmt.Sprintf("-spice port=%d", port)
  76. spiceCmd += desc.OptionsToString(spice.Options)
  77. opts = append(opts, spiceCmd)
  78. // intel-hda and codec hda-duplex
  79. opts = append(opts, generatePCIDeviceOption(spice.IntelHDA.PCIDevice))
  80. codec := spice.IntelHDA.Codec
  81. opts = append(opts,
  82. fmt.Sprintf("-device %s,id=%s,bus=%s.0,cad=%d",
  83. codec.Type, codec.Id, spice.IntelHDA.Id, codec.Cad),
  84. )
  85. // serial port
  86. opts = append(opts, generatePCIDeviceOption(spice.VdagentSerial.PCIDevice))
  87. opts = append(opts, chardevOption(spice.Vdagent))
  88. opts = append(opts, virtSerialPortOption(spice.VdagentSerialPort, spice.VdagentSerial.Id))
  89. // usb redirct
  90. opts = append(opts, usbRedirOptions(spice.UsbRedirct)...)
  91. return opts
  92. }
  93. func generatePciControllerOptions(controllers []*desc.PCIController) []string {
  94. opts := make([]string, 0)
  95. for i := 0; i < len(controllers); i++ {
  96. switch controllers[i].CType {
  97. case desc.CONTROLLER_TYPE_PCIE_TO_PCI_BRIDGE:
  98. opts = append(opts,
  99. fmt.Sprintf(
  100. "-device pcie-pci-bridge,id=pci.%d,bus=pcie.%d,addr=0x%02x",
  101. i, controllers[i].Bus, controllers[i].Slot,
  102. ))
  103. case desc.CONTROLLER_TYPE_PCI_BRIDGE:
  104. opts = append(opts,
  105. fmt.Sprintf(
  106. "-device pci-bridge,id=pci.%d,bus=pci.%d,chassis_nr=%d,addr=0x%02x",
  107. i, controllers[i].Bus, i, controllers[i].Slot),
  108. )
  109. case desc.CONTROLLER_TYPE_PCIE_ROOT_PORT:
  110. opts = append(opts,
  111. fmt.Sprintf(
  112. "-device ioh3420,id=pci.%d,chassis=%d,bus=pcie.%d,addr=0x%02x",
  113. i, i, controllers[i].Bus, controllers[i].Slot,
  114. ),
  115. )
  116. }
  117. }
  118. return opts
  119. }
  120. func generateNumaOption(memId string, nodeId *uint16, cpus *string) string {
  121. cmd := fmt.Sprintf("-numa node,memdev=%s", memId)
  122. if nodeId != nil {
  123. cmd += fmt.Sprintf(",nodeid=%d", *nodeId)
  124. }
  125. if cpus != nil {
  126. cpuSegs := strings.Split(*cpus, ",")
  127. for _, cpuSeg := range cpuSegs {
  128. cmd += fmt.Sprintf(",cpus=%s", cpuSeg)
  129. }
  130. }
  131. return cmd
  132. }
  133. func generateMemObjectWithNumaOptions(mem *desc.SMemDesc) string {
  134. cmds := []string{}
  135. cmds = append(cmds, generateObjectOption(mem.Object))
  136. cmds = append(cmds, generateNumaOption(mem.Id, mem.NodeId, mem.Cpus))
  137. return strings.Join(cmds, " ")
  138. }
  139. func generateMemoryOption(memDesc *desc.SGuestMem) string {
  140. cmds := []string{}
  141. cmds = append(cmds, fmt.Sprintf(
  142. "-m %dM,slots=%d,maxmem=%dM",
  143. memDesc.SizeMB, memDesc.Slots, memDesc.MaxMem,
  144. ))
  145. if memDesc.Mem != nil {
  146. cmds = append(cmds, generateMemObjectWithNumaOptions(&memDesc.Mem.SMemDesc))
  147. for i := range memDesc.Mem.Mems {
  148. cmds = append(cmds, generateMemObjectWithNumaOptions(&memDesc.Mem.Mems[i]))
  149. }
  150. }
  151. for i := 0; i < len(memDesc.MemSlots); i++ {
  152. memDev := memDesc.MemSlots[i].MemDev
  153. memObj := memDesc.MemSlots[i].MemObj
  154. cmds = append(cmds, generateObjectOption(memObj.Object))
  155. cmds = append(cmds, fmt.Sprintf("-device %s,id=%s,memdev=%s", memDev.Type, memDev.Id, memObj.Id))
  156. }
  157. return strings.Join(cmds, " ")
  158. }
  159. func generateMachineOption(drvOpt QemuOptions, desc *desc.SGuestDesc) string {
  160. cmd := fmt.Sprintf("-machine %s,accel=%s", desc.Machine, desc.MachineDesc.Accel)
  161. if desc.MachineDesc.GicVersion != nil {
  162. cmd += fmt.Sprintf(",gic-version=%s", *desc.MachineDesc.GicVersion)
  163. }
  164. if desc.NoHpet != nil && *desc.NoHpet {
  165. machineOpts, noHpetCmd := drvOpt.NoHpet()
  166. if machineOpts {
  167. cmd += fmt.Sprintf(",%s", noHpetCmd)
  168. } else if noHpetCmd != "" {
  169. cmd += fmt.Sprintf(" %s", noHpetCmd)
  170. }
  171. }
  172. return cmd
  173. }
  174. func generateSMPOption(guestDesc *desc.SGuestDesc) string {
  175. cpu := guestDesc.CpuDesc
  176. startCpus := cpu.Cpus
  177. if guestDesc.MemDesc.Mem != nil {
  178. if len(guestDesc.MemDesc.Mem.Mems) > 0 {
  179. startCpus = 1
  180. }
  181. }
  182. if cpu.MaxCpus%2 > 0 {
  183. return fmt.Sprintf(
  184. "-smp cpus=%d,maxcpus=%d", startCpus, cpu.MaxCpus,
  185. )
  186. } else {
  187. return fmt.Sprintf(
  188. "-smp cpus=%d,sockets=%d,cores=%d,maxcpus=%d",
  189. startCpus, cpu.Sockets, cpu.Cores, cpu.MaxCpus,
  190. )
  191. }
  192. }
  193. func generateCPUOption(cpu *desc.SGuestCpu) string {
  194. cmd := fmt.Sprintf("-cpu %s", cpu.Model)
  195. for feat, enable := range cpu.Features {
  196. if enable {
  197. cmd += "," + feat + "=on"
  198. } else {
  199. cmd += "," + feat + "=off"
  200. }
  201. }
  202. if len(cpu.Vendor) > 0 {
  203. cmd += fmt.Sprintf(",vendor=%s", cpu.Vendor)
  204. }
  205. if len(cpu.Level) > 0 {
  206. cmd += fmt.Sprintf(",level=%s", cpu.Level)
  207. }
  208. return cmd
  209. }
  210. func getMonitorOptions(drvOpt QemuOptions, input *Monitor) []string {
  211. if input == nil {
  212. return nil
  213. }
  214. idDev := input.Id + "dev"
  215. opts := []string{
  216. drvOpt.MonitorChardev(idDev, input.Port, "127.0.0.1"),
  217. drvOpt.Mon(idDev, input.Id, input.Mode),
  218. }
  219. return opts
  220. }
  221. func generateScsiOptions(scsi *desc.SGuestVirtioScsi) string {
  222. opt := generatePCIDeviceOption(scsi.PCIDevice)
  223. if scsi.NumQueues != nil && *scsi.NumQueues > 0 {
  224. opt += fmt.Sprintf(",num_queues=%d,vectors=%d", *scsi.NumQueues, *scsi.NumQueues+1)
  225. }
  226. return opt
  227. }
  228. func generateInitrdOptions(drvOpt QemuOptions, initrdPath, kernel string) []string {
  229. opts := make([]string, 0)
  230. opts = append(opts, drvOpt.Initrd(initrdPath))
  231. opts = append(opts, drvOpt.Kernel(kernel))
  232. opts = append(opts, "-append yn_rescue_mode=true")
  233. return opts
  234. }
  235. func generateKickstartBootOptions(drvOpt QemuOptions, kickstartBoot *KickstartBootInfo) []string {
  236. opts := make([]string, 0)
  237. opts = append(opts, drvOpt.Kernel(kickstartBoot.KernelPath))
  238. opts = append(opts, drvOpt.Initrd(kickstartBoot.InitrdPath))
  239. if kickstartBoot.KernelArgs != "" {
  240. // due to blank space in kickstart args, '' is needed
  241. opts = append(opts, fmt.Sprintf("-append '%s'", kickstartBoot.KernelArgs))
  242. }
  243. return opts
  244. }
  245. func generateDisksOptions(drvOpt QemuOptions, disks []*desc.SGuestDisk, isEncrypt, isMaster bool, osName string) []string {
  246. opts := make([]string, 0)
  247. for _, disk := range disks {
  248. if disk.Driver == api.DISK_DRIVER_VFIO {
  249. continue
  250. }
  251. if isMaster {
  252. opts = append(opts, getMasterDiskDriveOption(drvOpt, disk, isEncrypt))
  253. } else {
  254. opts = append(opts, getDiskDriveOption(drvOpt, disk, isEncrypt))
  255. }
  256. opts = append(opts, getDiskDeviceOption(drvOpt, disk, osName))
  257. }
  258. return opts
  259. }
  260. func getMasterDiskDriveOption(drvOpt QemuOptions, disk *desc.SGuestDisk, isEncrypt bool) string {
  261. format := disk.Format
  262. diskIndex := disk.Index
  263. cacheMode := disk.CacheMode
  264. aioMode := disk.AioMode
  265. opt := "if=none,driver=quorum,read-pattern=fifo,is-backup-mode=on,vote-threshold=1"
  266. opt += fmt.Sprintf(",id=drive_%d", diskIndex)
  267. opt += fmt.Sprintf(",cache=%s", cacheMode)
  268. if isLocalStorage(disk) {
  269. opt += fmt.Sprintf(",aio=%s", aioMode)
  270. }
  271. opt += fmt.Sprintf(",children.0.file.filename=$DISK_%d", diskIndex)
  272. if format == "raw" {
  273. opt += ",children.0.file.format=raw"
  274. }
  275. return drvOpt.Drive(opt)
  276. }
  277. func getDiskDriveOption(drvOpt QemuOptions, disk *desc.SGuestDisk, isEncrypt bool) string {
  278. format := disk.Format
  279. diskIndex := disk.Index
  280. cacheMode := disk.CacheMode
  281. aioMode := disk.AioMode
  282. var opt string
  283. opt = fmt.Sprintf("file=$DISK_%d", diskIndex)
  284. opt += ",if=none"
  285. opt += fmt.Sprintf(",id=drive_%d", diskIndex)
  286. if len(format) == 0 || format == "qcow2" {
  287. // pass # qemu will automatically detect image format
  288. } else if format == "raw" {
  289. opt += ",format=raw"
  290. }
  291. opt += fmt.Sprintf(",cache=%s", cacheMode)
  292. if isLocalStorage(disk) {
  293. opt += fmt.Sprintf(",aio=%s", aioMode)
  294. }
  295. if len(disk.Url) > 0 { // # a remote file backed image
  296. opt += ",copy-on-read=on"
  297. }
  298. if isLocalStorage(disk) {
  299. opt += ",file.locking=off"
  300. }
  301. if isEncrypt {
  302. opt += ",encrypt.format=luks,encrypt.key-secret=sec0"
  303. }
  304. if disk.AutoReset {
  305. opt += ",snapshot=on"
  306. }
  307. // #opt += ",media=disk"
  308. return drvOpt.Drive(opt)
  309. }
  310. func isLocalStorage(disk *desc.SGuestDisk) bool {
  311. if disk.StorageType == api.STORAGE_LOCAL || disk.StorageType == api.STORAGE_LVM || len(disk.StorageType) == 0 {
  312. return true
  313. } else {
  314. return false
  315. }
  316. }
  317. func getDiskDeviceOption(optDrv QemuOptions, disk *desc.SGuestDisk, osName string) string {
  318. diskIndex := disk.Index
  319. diskDriver := disk.Driver
  320. numQueues := disk.NumQueues
  321. isSsd := disk.IsSSD
  322. var opt = ""
  323. opt += GetDiskDeviceModel(diskDriver)
  324. if osName != OS_NAME_VMWARE {
  325. serial := strings.ReplaceAll(disk.DiskId, "-", "")
  326. opt += fmt.Sprintf(",serial=%s", serial)
  327. opt += optDrv.ScsiDeviceId(serial, diskDriver)
  328. }
  329. opt += fmt.Sprintf(",drive=drive_%d", diskIndex)
  330. if diskDriver == DISK_DRIVER_VIRTIO {
  331. // virtio-blk
  332. if disk.Pci != nil {
  333. opt += fmt.Sprintf(",bus=%s,addr=%s", disk.Pci.BusStr(), disk.Pci.SlotFunc())
  334. }
  335. // opt += fmt.Sprintf(",num-queues=%d,vectors=%d,iothread=iothread0", numQueues, numQueues+1)
  336. opt += ",iothread=iothread0"
  337. if numQueues > 0 {
  338. opt += fmt.Sprintf(",num-queues=%d,vectors=%d", numQueues, numQueues+1)
  339. }
  340. } else if utils.IsInStringArray(diskDriver, []string{DISK_DRIVER_SCSI, DISK_DRIVER_PVSCSI}) {
  341. opt += ",bus=scsi.0"
  342. } else if diskDriver == DISK_DRIVER_IDE {
  343. opt += fmt.Sprintf(",bus=ide.%d,unit=%d", diskIndex/2, diskIndex%2)
  344. } else if diskDriver == DISK_DRIVER_SATA {
  345. opt += fmt.Sprintf(",bus=ahci0.%d", diskIndex)
  346. }
  347. opt += fmt.Sprintf(",id=drive_%d", diskIndex)
  348. if isSsd {
  349. if diskDriver == DISK_DRIVER_SCSI {
  350. opt += ",rotation_rate=1"
  351. }
  352. }
  353. if disk.BootIndex != nil && *disk.BootIndex >= 0 {
  354. opt += fmt.Sprintf(",bootindex=%d", *disk.BootIndex)
  355. }
  356. return optDrv.Device(opt)
  357. }
  358. func generateCdromOptions(optDrv QemuOptions, cdroms []*desc.SGuestCdrom) []string {
  359. opts := make([]string, 0)
  360. for _, cdrom := range cdroms {
  361. //cdromDriveId := cdrom
  362. driveOpt := fmt.Sprintf("id=%s", cdrom.Id)
  363. driveOpt += desc.OptionsToString(cdrom.DriveOptions)
  364. var cdromPath = cdrom.Path
  365. if len(cdromPath) > 0 {
  366. driveOpt += fmt.Sprintf(",file=%s", cdromPath)
  367. }
  368. if cdrom.Ide != nil {
  369. opts = append(opts, optDrv.Drive(driveOpt))
  370. devOpt := fmt.Sprintf("%s,drive=%s,bus=ide.1",
  371. cdrom.Ide.DevType, cdrom.Id)
  372. if len(cdromPath) > 0 {
  373. if cdrom.BootIndex != nil && *cdrom.BootIndex >= 0 {
  374. devOpt += fmt.Sprintf(",bootindex=%d", *cdrom.BootIndex)
  375. }
  376. }
  377. // TODO: ,bus=ide.%d,unit=%d
  378. //, cdrom.Ide.Bus, cdrom.Ide.Unit)
  379. opts = append(opts, optDrv.Device(devOpt))
  380. } else if cdrom.Scsi != nil {
  381. if len(cdromPath) > 0 {
  382. opts = append(opts, optDrv.Drive(driveOpt))
  383. devOpt := fmt.Sprintf("%s,drive=%s,id=%s", cdrom.Scsi.DevType, cdrom.Id, cdrom.Scsi.Id)
  384. devOpt += desc.OptionsToString(cdrom.Scsi.Options)
  385. if len(cdromPath) > 0 {
  386. if cdrom.BootIndex != nil && *cdrom.BootIndex >= 0 {
  387. devOpt += fmt.Sprintf(",bootindex=%d", *cdrom.BootIndex)
  388. }
  389. }
  390. opts = append(opts, optDrv.Device(devOpt))
  391. }
  392. }
  393. }
  394. return opts
  395. }
  396. func generateFloppyOptions(optDrv QemuOptions, floppys []*desc.SGuestFloppy) []string {
  397. opts := make([]string, 0)
  398. for _, floppy := range floppys {
  399. driveOpt := fmt.Sprintf("id=%s", floppy.Id)
  400. driveOpt += desc.OptionsToString(floppy.DriveOptions)
  401. var floppyPath = floppy.Path
  402. if len(floppyPath) > 0 {
  403. driveOpt += fmt.Sprintf(",file=%s", floppyPath)
  404. }
  405. if floppy.Floppy != nil {
  406. opts = append(opts, optDrv.Drive(driveOpt))
  407. devOpt := fmt.Sprintf("%s,drive=%s",
  408. floppy.Floppy.DevType, floppy.Id)
  409. // TODO: ,bus=ide.%d,unit=%d
  410. //, cdrom.Ide.Bus, cdrom.Ide.Unit)
  411. opts = append(opts, optDrv.Device(devOpt))
  412. }
  413. }
  414. return opts
  415. }
  416. func GetDiskDeviceModel(driver string) string {
  417. if driver == DISK_DRIVER_VIRTIO {
  418. return "virtio-blk-pci"
  419. } else if utils.IsInStringArray(driver, []string{DISK_DRIVER_SCSI, DISK_DRIVER_PVSCSI}) {
  420. return "scsi-hd"
  421. } else if driver == DISK_DRIVER_IDE {
  422. return "ide-hd"
  423. } else if driver == DISK_DRIVER_SATA {
  424. return "ide-hd"
  425. } else {
  426. return "None"
  427. }
  428. }
  429. func generateNicOptions(drvOpt QemuOptions, input *GenerateStartOptionsInput) ([]string, error) {
  430. opts := make([]string, 0)
  431. nics := input.GuestDesc.Nics
  432. //input.guest
  433. for idx := range nics {
  434. if nics[idx].Driver == api.NETWORK_DRIVER_VFIO {
  435. continue
  436. }
  437. var nicTrafficExceed = false
  438. if input.NicTraffics != nil {
  439. nicTraffic, ok := input.NicTraffics[nics[idx].Mac]
  440. if ok && nicTraffic != nil {
  441. if nics[idx].TxTrafficLimit > 0 && nicTraffic.TxTraffic > nics[idx].TxTrafficLimit {
  442. nicTrafficExceed = true
  443. }
  444. if nics[idx].RxTrafficLimit > 0 && nicTraffic.RxTraffic > nics[idx].RxTrafficLimit {
  445. nicTrafficExceed = true
  446. }
  447. }
  448. }
  449. netDevOpt, err := getNicNetdevOption(drvOpt, nics[idx], input.IsKVMSupport, nicTrafficExceed)
  450. if err != nil {
  451. return nil, errors.Wrapf(err, "getNicNetdevOption %v", nics[idx])
  452. }
  453. opts = append(opts,
  454. netDevOpt,
  455. // aarch64 with addr lead to:
  456. // virtio_net: probe of virtioN failed with error -22
  457. getNicDeviceOption(drvOpt, nics[idx], input))
  458. }
  459. return opts, nil
  460. }
  461. func getNicNetdevOption(drvOpt QemuOptions, nic *desc.SGuestNetwork, isKVMSupport bool, nicTrafficExceed bool) (string, error) {
  462. if nic.Ifname == "" {
  463. return "", errors.Error("ifname is empty")
  464. }
  465. if nic.UpscriptPath == "" {
  466. return "", errors.Error("upscript_path is empty")
  467. }
  468. if nic.DownscriptPath == "" {
  469. return "", errors.Error("downscript_path is empty")
  470. }
  471. opt := "-netdev type=tap"
  472. opt += fmt.Sprintf(",id=%s", nic.Ifname)
  473. opt += fmt.Sprintf(",ifname=%s", nic.Ifname)
  474. if nic.Driver == "virtio" && isKVMSupport {
  475. opt += ",vhost=on,vhostforce=off"
  476. if nic.NumQueues > 1 {
  477. opt += fmt.Sprintf(",queues=%d", nic.NumQueues)
  478. }
  479. }
  480. if !nicTrafficExceed {
  481. opt += fmt.Sprintf(",script=%s", nic.UpscriptPath)
  482. }
  483. opt += fmt.Sprintf(",downscript=%s", nic.DownscriptPath)
  484. return opt, nil
  485. }
  486. func getNicDeviceOption(
  487. drvOpt QemuOptions,
  488. nic *desc.SGuestNetwork,
  489. input *GenerateStartOptionsInput,
  490. ) string {
  491. cmd := generatePCIDeviceOption(nic.Pci)
  492. cmd += fmt.Sprintf(",netdev=%s", nic.Ifname)
  493. cmd += fmt.Sprintf(",mac=%s", nic.Mac)
  494. if nic.Driver == "virtio" {
  495. if nic.NumQueues > 1 {
  496. cmd += ",mq=on"
  497. }
  498. if nic.Vectors != nil {
  499. cmd += fmt.Sprintf(",vectors=%d", *nic.Vectors)
  500. }
  501. cmd += fmt.Sprintf("$(nic_speed %d)", nic.Bw)
  502. if nic.Bridge == input.OVNIntegrationBridge {
  503. cmd += fmt.Sprintf("$(nic_mtu %q)", nic.Bridge)
  504. }
  505. }
  506. return cmd
  507. }
  508. func GetNicDeviceModel(name string) string {
  509. if name == "virtio" {
  510. return "virtio-net-pci"
  511. } else if name == "e1000" {
  512. return "e1000-82545em"
  513. } else {
  514. return name
  515. }
  516. }
  517. func generateUsbDeviceOption(usbControllerId string, usb *desc.UsbDevice) string {
  518. cmd := fmt.Sprintf("-device %s,bus=%s.0,id=%s", usb.DevType, usbControllerId, usb.Id)
  519. cmd += desc.OptionsToString(usb.Options)
  520. return cmd
  521. }
  522. func generateVfioDeviceOption(dev *desc.SGuestIsolatedDevice) []string {
  523. opts := make([]string, 0)
  524. for i := 0; i < len(dev.VfioDevs); i++ {
  525. cmd := generatePCIDeviceOption(dev.VfioDevs[i].PCIDevice)
  526. if dev.MdevId != "" {
  527. cmd += fmt.Sprintf(",sysfsdev=/sys/bus/mdev/devices/%s", dev.MdevId)
  528. } else {
  529. cmd += fmt.Sprintf(",host=%s", dev.VfioDevs[i].HostAddr)
  530. if dev.VfioDevs[i].XVga {
  531. cmd += ",x-vga=on"
  532. }
  533. }
  534. opts = append(opts, cmd)
  535. }
  536. return opts
  537. }
  538. func generateIsolatedDeviceOptions(guestDesc *desc.SGuestDesc) []string {
  539. opts := make([]string, 0)
  540. for i := 0; i < len(guestDesc.IsolatedDevices); i++ {
  541. if guestDesc.IsolatedDevices[i].Usb != nil {
  542. opts = append(opts,
  543. generateUsbDeviceOption(guestDesc.Usb.Id, guestDesc.IsolatedDevices[i].Usb),
  544. )
  545. //} else if guestDesc.IsolatedDevices[i].DevType {
  546. } else if len(guestDesc.IsolatedDevices[i].VfioDevs) > 0 {
  547. opts = append(opts,
  548. generateVfioDeviceOption(guestDesc.IsolatedDevices[i])...,
  549. )
  550. }
  551. }
  552. return opts
  553. }
  554. func generateObjectOption(o *desc.Object) string {
  555. cmd := fmt.Sprintf("-object %s,id=%s", o.ObjType, o.Id)
  556. cmd += desc.OptionsToString(o.Options)
  557. return cmd
  558. }
  559. func getRNGRandomOptions(rng *desc.SGuestRng) []string {
  560. cmd := generatePCIDeviceOption(rng.PCIDevice)
  561. cmd += fmt.Sprintf(",rng=%s", rng.RngRandom.Id)
  562. return []string{
  563. generateObjectOption(rng.RngRandom),
  564. cmd,
  565. }
  566. }
  567. func generateQgaOptions(guestDesc *desc.SGuestDesc) []string {
  568. opts := make([]string, 0)
  569. opts = append(opts, chardevOption(guestDesc.Qga.Socket))
  570. opts = append(opts, virtSerialPortOption(guestDesc.Qga.SerialPort, guestDesc.VirtioSerial.Id))
  571. return opts
  572. }
  573. func generateISASerialOptions(isaSerial *desc.SGuestIsaSerial) []string {
  574. opts := make([]string, 0)
  575. opts = append(opts, chardevOption(isaSerial.Pty))
  576. opts = append(opts, fmt.Sprintf("-device isa-serial,chardev=%s,id=%s", isaSerial.Pty.Id, isaSerial.Id))
  577. return opts
  578. }
  579. func generateKickstartSerialOptions(kickstartBoot *KickstartBootInfo) []string {
  580. if kickstartBoot == nil || kickstartBoot.SerialFilePath == "" {
  581. return nil
  582. }
  583. opts := make([]string, 0)
  584. chardevId := "kickstart_serial"
  585. // Create chardev with file backend
  586. chardevOpt := fmt.Sprintf("-chardev file,path=%s,id=%s", kickstartBoot.SerialFilePath, chardevId)
  587. opts = append(opts, chardevOpt)
  588. // Create ISA serial device
  589. serialOpt := fmt.Sprintf("-device isa-serial,chardev=%s,id=kickstart_serial_device", chardevId)
  590. opts = append(opts, serialOpt)
  591. return opts
  592. }
  593. func generatePvpanicDeviceOption(pvpanic *desc.SGuestPvpanic) string {
  594. return fmt.Sprintf("-device pvpanic,id=%s,ioport=0x%x", pvpanic.Id, pvpanic.Ioport)
  595. }
  596. func generateTpmDevOptions(tpm *desc.SGuestTpm) []string {
  597. opts := make([]string, 0)
  598. opts = append(opts, chardevOption(tpm.TpmSock))
  599. opts = append(opts, fmt.Sprintf("-tpmdev emulator,id=%s,chardev=%s", tpm.Id, tpm.TpmSock.Id))
  600. opts = append(opts, fmt.Sprintf("-device tpm-tis,tpmdev=%s", tpm.Id))
  601. return opts
  602. }
  603. func getMigrateOptions(drvOpt QemuOptions, input *GenerateStartOptionsInput) []string {
  604. opts := make([]string, 0)
  605. if input.NeedMigrate {
  606. if input.LiveMigrateUseTLS {
  607. opts = append(opts, "-incoming defer")
  608. } else {
  609. opts = append(opts, fmt.Sprintf("-incoming tcp:[::]:%d", input.LiveMigratePort))
  610. }
  611. } else if input.GuestDesc.IsSlave {
  612. opts = append(opts, fmt.Sprintf("-incoming tcp:[::]:%d", input.LiveMigratePort))
  613. }
  614. return opts
  615. }
  616. type GenerateStartOptionsInput struct {
  617. QemuVersion Version
  618. QemuArch Arch
  619. GuestDesc *desc.SGuestDesc
  620. IsKVMSupport bool
  621. NicTraffics map[string]*api.SNicTrafficRecord
  622. EnableUUID bool
  623. OsName string
  624. HugepagesEnabled bool
  625. EnableMemfd bool
  626. EnableTpm bool
  627. OVNIntegrationBridge string
  628. Devices []string
  629. OVMFPath string
  630. OVMFVarsPath string
  631. VNCPort uint
  632. VNCPassword bool
  633. EnableLog bool
  634. HMPMonitor *Monitor
  635. QMPMonitor *Monitor
  636. IsVdiSpice bool
  637. SpicePort uint
  638. PidFilePath string
  639. HomeDir string
  640. ExtraOptions []string
  641. EnableRNGRandom bool
  642. EnableSerialDevice bool
  643. NeedMigrate bool
  644. LiveMigratePort uint
  645. LiveMigrateUseTLS bool
  646. EnablePvpanic bool
  647. EncryptKeyPath string
  648. RescueInitrdPath string // rescue initramfs path
  649. RescueKernelPath string // rescue kernel path
  650. KickstartBoot *KickstartBootInfo
  651. }
  652. type KickstartBootInfo struct {
  653. Config *api.KickstartConfig
  654. MountPath string
  655. KernelPath string
  656. InitrdPath string
  657. KernelArgs string
  658. SerialFilePath string
  659. ConfigIsoPath string
  660. }
  661. func (input *GenerateStartOptionsInput) HasBootIndex() bool {
  662. for _, cdrom := range input.GuestDesc.Cdroms {
  663. if cdrom.BootIndex != nil && *cdrom.BootIndex >= 0 {
  664. return true
  665. }
  666. }
  667. for _, disk := range input.GuestDesc.Disks {
  668. if disk.BootIndex != nil && *disk.BootIndex >= 0 {
  669. return true
  670. }
  671. }
  672. return false
  673. }
  674. func GenerateStartOptions(
  675. input *GenerateStartOptionsInput,
  676. ) (string, error) {
  677. drv, ok := GetCommand(input.QemuVersion, input.QemuArch)
  678. if !ok {
  679. return "", errors.Errorf("Qemu comand driver %s %s not registered", input.QemuVersion, input.QemuArch)
  680. }
  681. drvOpt := drv.GetOptions()
  682. opts := make([]string, 0)
  683. // generate cpu options
  684. cpuOpt := generateCPUOption(input.GuestDesc.CpuDesc)
  685. opts = append(opts, drvOpt.FreezeCPU(), cpuOpt)
  686. if input.EnableLog {
  687. opts = append(opts, drvOpt.Log(input.EnableLog))
  688. }
  689. // TODO hmp - -
  690. opts = append(opts, getMonitorOptions(drvOpt, input.HMPMonitor)...)
  691. if input.QMPMonitor != nil {
  692. opts = append(opts, getMonitorOptions(drvOpt, input.QMPMonitor)...)
  693. }
  694. opts = append(opts,
  695. drvOpt.RTC(),
  696. // drvOpt.Daemonize(),
  697. drvOpt.Nodefaults(),
  698. drvOpt.Nodefconfig(),
  699. // drvOpt.NoKVMPitReinjection(),
  700. drvOpt.Global(),
  701. generateMachineOption(drvOpt, input.GuestDesc),
  702. drvOpt.KeyboardLayoutLanguage("en-us"),
  703. generateSMPOption(input.GuestDesc),
  704. drvOpt.Name(input.GuestDesc.Name),
  705. drvOpt.UUID(input.EnableUUID, input.GuestDesc.Uuid),
  706. generateMemoryOption(input.GuestDesc.MemDesc),
  707. )
  708. // bootOrder
  709. enableMenu := false
  710. for _, cdrom := range input.GuestDesc.Cdroms {
  711. if cdrom.Path != "" {
  712. enableMenu = true
  713. }
  714. }
  715. // Note that it does not make sense to use the bootindex property together
  716. // with the "-boot order=..." (or "-boot once=...") parameter.
  717. var bootOrder *string
  718. if !input.HasBootIndex() {
  719. bootOrder = &input.GuestDesc.BootOrder
  720. }
  721. opts = append(opts, drvOpt.Boot(bootOrder, enableMenu))
  722. // bios
  723. if input.GuestDesc.Bios == BIOS_UEFI {
  724. if input.OVMFPath == "" {
  725. return "", errors.Errorf("input OVMF path is empty")
  726. }
  727. fmOpt, err := drvOpt.BIOS(input.OVMFPath, input.OVMFVarsPath, input.HomeDir)
  728. if err != nil {
  729. return "", errors.Wrap(err, "bios option")
  730. }
  731. opts = append(opts, fmOpt)
  732. }
  733. if input.OsName == OS_NAME_MACOS {
  734. opts = append(opts, drvOpt.Device("isa-applesmc,osk=ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc"))
  735. }
  736. if input.GuestDesc.Vga != "none" && input.GuestDesc.VgaDevice != nil {
  737. opts = append(opts, generatePCIDeviceOption(input.GuestDesc.VgaDevice.PCIDevice))
  738. }
  739. // vdi spice
  740. if input.IsVdiSpice && input.GuestDesc.VdiDevice != nil && input.GuestDesc.VdiDevice.Spice != nil {
  741. opts = append(opts, generateSpiceOptions(input.SpicePort, input.GuestDesc.VdiDevice.Spice)...)
  742. } else {
  743. opts = append(opts, drvOpt.VNC(input.VNCPort, input.VNCPassword))
  744. }
  745. // iothread object
  746. opts = append(opts, drvOpt.Object("iothread", map[string]string{"id": "iothread0"}))
  747. isEncrypt := false
  748. if len(input.EncryptKeyPath) > 0 {
  749. opts = append(opts, drvOpt.Object("secret", map[string]string{"id": "sec0", "file": input.EncryptKeyPath, "format": "base64"}))
  750. isEncrypt = true
  751. }
  752. if input.GuestDesc.VirtioSerial != nil {
  753. opts = append(opts, generatePCIDeviceOption(input.GuestDesc.VirtioSerial.PCIDevice))
  754. }
  755. opts = append(opts, generatePciControllerOptions(input.GuestDesc.PCIControllers)...)
  756. if input.GuestDesc.VirtioScsi != nil {
  757. opts = append(opts, generateScsiOptions(input.GuestDesc.VirtioScsi))
  758. } else if input.GuestDesc.PvScsi != nil {
  759. opts = append(opts, generatePCIDeviceOption(input.GuestDesc.PvScsi.PCIDevice))
  760. }
  761. if input.GuestDesc.SataController != nil {
  762. opts = append(opts, generatePCIDeviceOption(input.GuestDesc.SataController.PCIDevice))
  763. }
  764. // generate initrd and kernel options
  765. if input.GuestDesc.LightMode {
  766. opts = append(opts, generateInitrdOptions(
  767. drvOpt,
  768. input.RescueInitrdPath,
  769. input.RescueKernelPath,
  770. )...)
  771. } else if input.KickstartBoot != nil {
  772. opts = append(opts, generateKickstartBootOptions(
  773. drvOpt,
  774. input.KickstartBoot,
  775. )...)
  776. }
  777. // generate disk options
  778. opts = append(opts, generateDisksOptions(
  779. drvOpt, input.GuestDesc.Disks, isEncrypt, input.GuestDesc.IsMaster, input.OsName)...)
  780. // cdrom
  781. opts = append(opts, generateCdromOptions(drvOpt, input.GuestDesc.Cdroms)...)
  782. //floppy
  783. opts = append(opts, generateFloppyOptions(drvOpt, input.GuestDesc.Floppys)...)
  784. // generate nics
  785. nicOpts, err := generateNicOptions(drvOpt, input)
  786. if err != nil {
  787. return "", errors.Wrap(err, "generateNicOptions")
  788. }
  789. opts = append(opts, nicOpts...)
  790. if !input.GuestDesc.LightMode {
  791. if !input.QemuArch.IsX86() {
  792. if input.GuestDesc.Usb != nil {
  793. opts = append(opts, generatePCIDeviceOption(input.GuestDesc.Usb.PCIDevice))
  794. for _, device := range input.Devices {
  795. opts = append(opts, drvOpt.Device(device))
  796. }
  797. }
  798. } else {
  799. opts = append(opts, drvOpt.USB())
  800. for _, device := range input.Devices {
  801. opts = append(opts, drvOpt.Device(device))
  802. }
  803. if input.GuestDesc.Usb != nil {
  804. opts = append(opts, generatePCIDeviceOption(input.GuestDesc.Usb.PCIDevice))
  805. }
  806. }
  807. }
  808. // isolated devices
  809. if len(input.GuestDesc.IsolatedDevices) > 0 && !input.GuestDesc.LightMode {
  810. opts = append(opts, generateIsolatedDeviceOptions(input.GuestDesc)...)
  811. }
  812. // pidfile
  813. opts = append(opts, drvOpt.Pidfile(input.PidFilePath))
  814. // qga
  815. // opts = append(opts, drvOpt.QGA(input.HomeDir)...)
  816. if input.GuestDesc.Qga != nil {
  817. opts = append(opts, generateQgaOptions(input.GuestDesc)...)
  818. }
  819. // random device
  820. if input.GuestDesc.Rng != nil {
  821. opts = append(opts, getRNGRandomOptions(input.GuestDesc.Rng)...)
  822. }
  823. // serial device
  824. if input.GuestDesc.IsaSerial != nil {
  825. opts = append(opts, generateISASerialOptions(input.GuestDesc.IsaSerial)...)
  826. }
  827. // kickstart serial device
  828. if input.KickstartBoot != nil {
  829. opts = append(opts, generateKickstartSerialOptions(input.KickstartBoot)...)
  830. }
  831. // migrate options
  832. opts = append(opts, getMigrateOptions(drvOpt, input)...)
  833. // pvpanic device
  834. if input.GuestDesc.Pvpanic != nil {
  835. opts = append(opts, generatePvpanicDeviceOption(input.GuestDesc.Pvpanic))
  836. }
  837. if input.GuestDesc.Tpm != nil {
  838. opts = append(opts, generateTpmDevOptions(input.GuestDesc.Tpm)...)
  839. }
  840. // move extra options to end of cmdline
  841. if len(input.ExtraOptions) != 0 {
  842. opts = append(opts, input.ExtraOptions...)
  843. }
  844. return strings.Join(opts, " "), nil
  845. }