| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563 |
- // 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 guestman
- import (
- "context"
- "fmt"
- "net"
- "os"
- "path"
- "path/filepath"
- "strconv"
- "strings"
- "time"
- "github.com/mdlayher/arp"
- "github.com/mdlayher/ethernet"
- "github.com/sergi/go-diff/diffmatchpatch"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/onecloud/pkg/apis"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/hostman/guestman/desc"
- "yunion.io/x/onecloud/pkg/hostman/guestman/qemu"
- qemucerts "yunion.io/x/onecloud/pkg/hostman/guestman/qemu/certs"
- deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
- "yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
- "yunion.io/x/onecloud/pkg/hostman/hostdeployer/uefi"
- "yunion.io/x/onecloud/pkg/hostman/monitor"
- "yunion.io/x/onecloud/pkg/hostman/options"
- "yunion.io/x/onecloud/pkg/hostman/storageman"
- "yunion.io/x/onecloud/pkg/util/fileutils2"
- "yunion.io/x/onecloud/pkg/util/mountutils"
- "yunion.io/x/onecloud/pkg/util/procutils"
- "yunion.io/x/onecloud/pkg/util/qemutils"
- )
- const (
- OS_NAME_LINUX = qemu.OS_NAME_LINUX
- OS_NAME_WINDOWS = qemu.OS_NAME_WINDOWS
- OS_NAME_MACOS = qemu.OS_NAME_MACOS
- OS_NAME_ANDROID = qemu.OS_NAME_ANDROID
- OS_NAME_VMWARE = qemu.OS_NAME_VMWARE
- OS_NAME_CIRROS = qemu.OS_NAME_CIRROS
- OS_NAME_OPENWRT = qemu.OS_NAME_OPENWRT
- MODE_READLINE = qemu.MODE_READLINE
- MODE_CONTROL = qemu.MODE_CONTROL
- DISK_DRIVER_VIRTIO = qemu.DISK_DRIVER_VIRTIO
- DISK_DRIVER_SCSI = qemu.DISK_DRIVER_SCSI
- DISK_DRIVER_PVSCSI = qemu.DISK_DRIVER_PVSCSI
- DISK_DRIVER_IDE = qemu.DISK_DRIVER_IDE
- DISK_DRIVER_SATA = qemu.DISK_DRIVER_SATA
- )
- const guestLauncher = `#!/usr/bin/env python
- import sys
- import os
- import time
- import subprocess
- import shlex
- with open(os.devnull, 'w') as FNULL:
- try:
- cmd_str = subprocess.check_output(['bash', '%s'], stderr=FNULL).decode('utf-8').strip()
- cmd = shlex.split(cmd_str)
- except BaseException as e:
- sys.stderr.write('%%s' %% e)
- sys.exit(1)
- statePath = '%s'
- if os.path.exists(statePath):
- cmd += ['--incoming', 'exec: cat %%s' %% statePath]
- elif os.path.exists('%%s/content' %% statePath):
- cmd += ['--incoming', 'exec: cat %%s/content' %% statePath]
- pid = os.fork()
- if pid < 0:
- sys.stderr.write('failed fork child process')
- sys.exit(1)
- if pid > 0:
- status_encoded = os.waitpid(pid, 0)[1]
- sys.exit((status_encoded>>8) & 0xff)
- else:
- os.setsid()
- os.chdir('/')
- pid = os.fork()
- if pid < 0:
- sys.stderr.write('failed fork child process')
- sys.exit(1)
- if pid > 0:
- sys.stdout.write('%%d' %% pid)
- sys.exit(0)
- else:
- devnull = os.open('/dev/null', os.O_RDWR)
- os.dup2(devnull, 0)
- os.close(devnull)
- logfd = os.open('%s', os.O_RDWR|os.O_CREAT|os.O_APPEND)
- os.dup2(logfd, 1)
- os.dup2(logfd, 2)
- os.write(logfd, str.encode('%%s Run command: %%s\n' %% (time.strftime('%%Y-%%m-%%d %%H:%%M:%%S', time.localtime()), cmd)))
- os.close(logfd)
- if os.execv(cmd[0], cmd) < 0:
- sys.stderr.write('exec error')
- sys.exit(1)
- `
- func (s *SKVMGuestInstance) IsKvmSupport() bool {
- return s.manager.GetHost().IsKvmSupport()
- }
- func (s *SKVMGuestInstance) IsNestedVirt() bool {
- return s.manager.GetHost().IsNestedVirtualization()
- }
- func (s *SKVMGuestInstance) GetKernelVersion() string {
- return s.manager.host.GetKernelVersion()
- }
- func (s *SKVMGuestInstance) HideHypervisor() bool {
- if s.IsRunning() && s.IsMonitorAlive() {
- cmdline, _ := fileutils2.FileGetContents(path.Join("/proc", strconv.Itoa(s.GetPid()), "cmdline"))
- if strings.Contains(cmdline, "hypervisor=off") {
- return true
- }
- }
- if s.hasGPU() && s.GetOsName() == OS_NAME_WINDOWS {
- return true
- }
- return false
- }
- func (s *SKVMGuestInstance) HideKVM() bool {
- if s.IsRunning() && s.IsMonitorAlive() {
- cmdline, _ := fileutils2.FileGetContents(path.Join("/proc", strconv.Itoa(s.GetPid()), "cmdline"))
- if strings.Contains(cmdline, "kvm=off") {
- return true
- }
- }
- if s.hasGPU() && s.GetOsName() != OS_NAME_WINDOWS {
- return true
- }
- return false
- }
- func (s *SKVMGuestInstance) CpuMax() (uint, error) {
- cpuMax, ok := s.manager.qemuMachineCpuMax[s.Desc.Machine]
- if !ok {
- return 0, errors.Errorf("unsupported cpu max for qemu machine: %s", s.Desc.Machine)
- }
- return cpuMax, nil
- }
- func (s *SKVMGuestInstance) IsVdiSpice() bool {
- return s.Desc.Vdi == "spice"
- }
- func (s *SKVMGuestInstance) GetOsName() string {
- if osName, ok := s.Desc.Metadata["os_name"]; ok {
- return osName
- }
- return OS_NAME_LINUX
- }
- func (s *SKVMGuestInstance) disableUsbKbd() bool {
- return s.Desc.Metadata["disable_usb_kbd"] == "true"
- }
- func (s *SKVMGuestInstance) getOsDistribution() string {
- return s.Desc.Metadata["os_distribution"]
- }
- func (s *SKVMGuestInstance) getOsVersion() string {
- return s.Desc.Metadata["os_version"]
- }
- func (s *SKVMGuestInstance) getOsCurrentVersion() string {
- return s.Desc.Metadata["os_current_version"]
- }
- func (s *SKVMGuestInstance) pciInitialized() bool {
- return len(s.Desc.PCIControllers) > 0
- }
- func (s *SKVMGuestInstance) hasPcieExtendBus() bool {
- return s.Desc.Metadata["__pcie_extend_bus"] == "true"
- }
- func (s *SKVMGuestInstance) setPcieExtendBus() {
- s.Desc.Metadata["__pcie_extend_bus"] = "true"
- }
- func (s *SKVMGuestInstance) getUsbControllerType() string {
- usbContType := s.Desc.Metadata["usb_controller_type"]
- if usbContType == "usb-ehci" {
- return usbContType
- } else {
- return "qemu-xhci"
- }
- }
- // is windows prioer to windows server 2003
- func (s *SKVMGuestInstance) IsOldWindows() bool {
- if s.GetOsName() == OS_NAME_WINDOWS {
- cv := s.getOsCurrentVersion()
- if len(cv) > 0 {
- if len(cv) > 1 && cv[0:2] == "5." {
- return true
- }
- } else {
- ver := s.getOsVersion()
- if len(ver) > 1 && ver[0:2] == "5." {
- return true
- }
- }
- }
- return false
- }
- func (s *SKVMGuestInstance) isWindows10() bool {
- if s.GetOsName() == OS_NAME_WINDOWS {
- distro := s.getOsDistribution()
- if strings.Contains(strings.ToLower(distro), "windows 10") {
- return true
- }
- osVer := s.getOsVersion()
- if strings.Contains(strings.ToLower(osVer), "windows 10") {
- return true
- }
- }
- return false
- }
- func (s *SKVMGuestInstance) isMemcleanEnabled() bool {
- return s.Desc.Metadata[api.VM_METADATA_ENABLE_MEMCLEAN] == "true"
- }
- func (s *SKVMGuestInstance) isDisableAutoMergeSnapshots() bool {
- return s.Desc.Metadata[api.VM_METADATA_DISABLE_AUTO_MERGE_SNAPSHOT] == "true"
- }
- func (s *SKVMGuestInstance) getMachine() string {
- machine := s.Desc.Machine
- if machine == "" {
- machine = api.VM_MACHINE_TYPE_PC
- }
- return machine
- }
- func (s *SKVMGuestInstance) getBios() string {
- bios := s.Desc.Bios
- if bios == "" {
- bios = api.VM_BOOT_MODE_BIOS
- }
- return bios
- }
- func (s *SKVMGuestInstance) isQ35() bool {
- return s.getMachine() == api.VM_MACHINE_TYPE_Q35
- }
- func (s *SKVMGuestInstance) isVirt() bool {
- return s.getMachine() == api.VM_MACHINE_TYPE_VIRT
- }
- func (s *SKVMGuestInstance) isPcie() bool {
- return utils.IsInStringArray(s.getMachine(),
- []string{api.VM_MACHINE_TYPE_Q35, api.VM_MACHINE_TYPE_VIRT})
- }
- func (s *SKVMGuestInstance) GetVdiProtocol() string {
- vdi := s.Desc.Vdi
- if vdi == "" {
- vdi = "vnc"
- }
- return vdi
- }
- func (s *SKVMGuestInstance) GetPciBus() string {
- if s.isQ35() || s.isVirt() {
- return "pcie.0"
- } else {
- return "pci.0"
- }
- }
- func (s *SKVMGuestInstance) disableIsaSerialDev() bool {
- return s.Desc.Metadata["disable_isa_serial"] == "true"
- }
- func (s *SKVMGuestInstance) disablePvpanicDev() bool {
- return s.Desc.Metadata["disable_pvpanic"] == "true"
- }
- func (s *SKVMGuestInstance) enableTpmDev() bool {
- return s.Desc.Metadata[api.VM_METADATA_ENABLE_TPM] == "true"
- }
- func (s *SKVMGuestInstance) getQuorumChildIndex() int64 {
- if sidx, ok := s.Desc.Metadata[api.QUORUM_CHILD_INDEX]; ok {
- idx, _ := strconv.ParseInt(sidx, 10, 0)
- return idx
- }
- return 0
- }
- func (s *SKVMGuestInstance) getNicUpScriptPath(nic *desc.SGuestNetwork) string {
- dev := s.manager.GetHost().GetBridgeDev(nic.Bridge)
- return path.Join(s.HomeDir(), fmt.Sprintf("if-up-%s-%s.sh", dev.Bridge(), nic.Ifname))
- }
- func (s *SKVMGuestInstance) getNicDownScriptPath(nic *desc.SGuestNetwork) string {
- dev := s.manager.GetHost().GetBridgeDev(nic.Bridge)
- return path.Join(s.HomeDir(), fmt.Sprintf("if-down-%s-%s.sh", dev.Bridge(), nic.Ifname))
- }
- func (s *SKVMGuestInstance) generateNicScripts(nic *desc.SGuestNetwork) error {
- bridge := nic.Bridge
- dev := s.manager.GetHost().GetBridgeDev(bridge)
- if dev == nil {
- return fmt.Errorf("Can't find bridge %s", bridge)
- }
- isVolatileHost := s.IsSlave() || s.IsMigratingDestGuest()
- if err := dev.GenerateIfupScripts(s.getNicUpScriptPath(nic), nic, isVolatileHost); err != nil {
- return errors.Wrap(err, "GenerateIfupScripts")
- }
- if err := dev.GenerateIfdownScripts(s.getNicDownScriptPath(nic), nic, isVolatileHost); err != nil {
- return errors.Wrap(err, "GenerateIfdownScripts")
- }
- return nil
- }
- func (s *SKVMGuestInstance) getNicDeviceModel(name string) string {
- return qemu.GetNicDeviceModel(name)
- }
- func (s *SKVMGuestInstance) extraOptions() string {
- cmd := " "
- for k, v := range s.Desc.ExtraOptions {
- switch jsonV := v.(type) {
- case *jsonutils.JSONArray:
- for i := 0; i < jsonV.Size(); i++ {
- vAtI, _ := jsonV.GetAt(i)
- vStr, _ := vAtI.GetString()
- cmd += fmt.Sprintf(" -%s %s", k, vStr)
- }
- default:
- vstr, _ := v.GetString()
- cmd += fmt.Sprintf(" -%s %s", k, vstr)
- }
- }
- return cmd
- }
- func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (string, error) {
- // initial data
- var input = &qemu.GenerateStartOptionsInput{
- GuestDesc: s.Desc,
- OsName: s.GetOsName(),
- OVNIntegrationBridge: options.HostOptions.OvnIntegrationBridge,
- HomeDir: s.HomeDir(),
- HugepagesEnabled: s.manager.host.IsHugepagesEnabled(),
- EnableMemfd: s.isMemcleanEnabled(),
- EnableTpm: s.enableTpmDev(),
- PidFilePath: s.GetPidFilePath(),
- }
- if data.Contains("encrypt_key") {
- key, _ := data.GetString("encrypt_key")
- if err := s.saveEncryptKeyFile(key); err != nil {
- return "", errors.Wrap(err, "save encrypt key file")
- }
- input.EncryptKeyPath = s.getEncryptKeyPath()
- }
- cmd := ""
- // inject vncPort
- vncPort, _ := data.Int("vnc_port")
- input.VNCPort = uint(vncPort)
- // inject qemu version and arch
- qemuVersion := options.HostOptions.DefaultQemuVersion
- if data.Contains("qemu_version") {
- qemuVersion, _ = data.GetString("qemu_version")
- }
- if qemuVersion == "latest" {
- qemuVersion = ""
- }
- input.QemuVersion = qemu.Version(qemuVersion)
- // inject qemu arch
- if s.manager.host.IsAarch64() {
- input.QemuArch = qemu.Arch_aarch64
- } else if s.manager.host.IsRiscv64() {
- input.QemuArch = qemu.Arch_riscv64
- } else {
- input.QemuArch = qemu.Arch_x86_64
- }
- for _, nic := range s.Desc.Nics {
- if nic.Driver == api.NETWORK_DRIVER_VFIO {
- continue
- }
- downscript := s.getNicDownScriptPath(nic)
- cmd += fmt.Sprintf("%s %s\n", downscript, nic.Ifname)
- }
- traffic, err := guestManager.GetGuestTrafficRecord(s.Id)
- if err != nil {
- return "", errors.Wrap(err, "get guest traffic record")
- }
- input.NicTraffics = traffic
- if input.HugepagesEnabled {
- cmd += fmt.Sprintf("mkdir -p /dev/hugepages/%s\n", s.Desc.Uuid)
- cmd += fmt.Sprintf("mount -t hugetlbfs -o pagesize=%dK,size=%dM hugetlbfs-%s /dev/hugepages/%s\n",
- s.manager.host.HugepageSizeKb(), s.Desc.Mem, s.Desc.Uuid, s.Desc.Uuid)
- }
- cmd += "sleep 1\n"
- cmd += fmt.Sprintf("echo %d > %s\n", input.VNCPort, s.GetVncFilePath())
- diskScripts, err := s.generateDiskSetupScripts(s.Desc.Disks)
- if err != nil {
- return "", errors.Wrap(err, "generateDiskSetupScripts")
- }
- cmd += diskScripts
- sriovInitScripts, err := s.generateSRIOVInitScripts()
- if err != nil {
- return "", errors.Wrap(err, "generateSRIOVInitScripts")
- }
- cmd += sriovInitScripts
- // cmd += fmt.Sprintf("STATE_FILE=`ls -d %s* | head -n 1`\n", s.getStateFilePathRootPrefix())
- cmd += fmt.Sprintf("PID_FILE=%s\n", input.PidFilePath)
- var qemuCmd = qemutils.GetQemu(string(input.QemuVersion))
- if len(qemuCmd) == 0 {
- qemuCmd = qemutils.GetQemu("")
- }
- cmd += fmt.Sprintf("DEFAULT_QEMU_CMD='%s'\n", qemuCmd)
- /*
- * cmd += "if [ -n \"$STATE_FILE\" ]; then\n"
- * cmd += " QEMU_VER=`echo $STATE_FILE" +
- * ` | grep -o '_[[:digit:]]\+\.[[:digit:]]\+.*'` + "`\n"
- * cmd += " QEMU_CMD=\"qemu-system-x86_64\"\n"
- * cmd += " QEMU_LOCAL_PATH=\"/usr/local/bin/$QEMU_CMD\"\n"
- * cmd += " QEMU_LOCAL_PATH_VER=\"/usr/local/qemu-$QEMU_VER/bin/$QEMU_CMD\"\n"
- * cmd += " QEMU_BIN_PATH=\"/usr/bin/$QEMU_CMD\"\n"
- * cmd += " if [ -f \"$QEMU_LOCAL_PATH_VER\" ]; then\n"
- * cmd += " QEMU_CMD=$QEMU_LOCAL_PATH_VER\n"
- * cmd += " elif [ -f \"$QEMU_LOCAL_PATH\" ]; then\n"
- * cmd += " QEMU_CMD=$QEMU_LOCAL_PATH\n"
- * cmd += " elif [ -f \"$QEMU_BIN_PATH\" ]; then\n"
- * cmd += " QEMU_CMD=$QEMU_BIN_PATH\n"
- * cmd += " fi\n"
- * cmd += "else\n"
- * cmd += " QEMU_CMD=$DEFAULT_QEMU_CMD\n"
- * cmd += "fi\n"
- */
- cmd += "QEMU_CMD=$DEFAULT_QEMU_CMD\n"
- if s.IsKvmSupport() && !options.HostOptions.DisableKVM {
- cmd += "QEMU_CMD_KVM_ARG=-enable-kvm\n"
- } else if utils.IsInStringArray(s.manager.host.GetCpuArchitecture(), apis.ARCH_X86) {
- // -no-kvm仅x86适用,且将在qemu 5.2之后移除
- // https://gitlab.com/qemu-project/qemu/-/blob/master/docs/about/removed-features.rst
- cmd += "QEMU_CMD_KVM_ARG=-no-kvm\n"
- } else {
- cmd += "QEMU_CMD_KVM_ARG=\n"
- }
- // cmd += "fi\n"
- cmd += `
- function nic_speed() {
- $QEMU_CMD $QEMU_CMD_KVM_ARG -device virtio-net-pci,help 2>&1 | grep -q "\<speed="
- if [ "$?" -eq "0" ]; then
- echo ",speed=$1"
- fi
- }
- function nic_mtu() {
- local bridge="$1"; shift
- $QEMU_CMD $QEMU_CMD_KVM_ARG -device virtio-net-pci,help 2>&1 | grep -q '\<host_mtu='
- if [ "$?" -eq "0" ]; then
- local origmtu="$(<"/sys/class/net/$bridge/mtu")"
- if [ -n "$origmtu" -a "$origmtu" -gt 576 ]; then
- echo ",host_mtu=$(($origmtu - ` + api.VpcOvnEncapCostStr() + `))"
- fi
- fi
- }
- `
- if input.EnableTpm {
- input.OVMFPath = options.HostOptions.SecbootOvmfPath
- input.OVMFVarsPath = options.HostOptions.SecbootOvmfVarsPath
- cmd += `
- function start_swtpm() {
- local swtpm_binary=$1
- local swtpm_dir=$2
- local swtpm_socket=$swtpm_dir/swtpm.sock
- local swtpm_log=$swtpm_dir/swtpm.log
- local swtpm_pid=$swtpm_dir/swtpm.pid
- if [ -f "$swtpm_pid" ] && ps -p $(cat "$swtpm_pid") >/dev/null 2>&1; then
- return 0
- fi
- mkdir -p $swtpm_dir
- $swtpm_binary socket --tpmstate dir=$swtpm_dir --ctrl type=unixio,path=$swtpm_socket --log file=$swtpm_log,level=20 --pid file=$swtpm_pid --tpm2 -d
- }
- `
- cmd += fmt.Sprintf("start_swtpm %s %s\n", options.HostOptions.BinarySwtpmPath, s.getSwtpmDirPath())
- }
- // Generate Start VM script
- cmd += `CMD="$QEMU_CMD $QEMU_CMD_KVM_ARG`
- if options.HostOptions.EnableQemuDebugLog {
- input.EnableLog = true
- }
- // inject monitor
- input.HMPMonitor = &qemu.Monitor{
- Id: "hmqmon",
- Port: uint(s.GetHmpMonitorPort(int(input.VNCPort))),
- Mode: MODE_READLINE,
- }
- input.QMPMonitor = &qemu.Monitor{
- Id: "qmqmon",
- Port: uint(s.GetQmpMonitorPort(int(input.VNCPort))),
- Mode: MODE_CONTROL,
- }
- input.EnableUUID = options.HostOptions.EnableVmUuid
- if s.Desc.Bios == qemu.BIOS_UEFI {
- if len(input.OVMFPath) == 0 {
- input.OVMFPath, input.OVMFVarsPath = s.getOvmfVarsSourcePath()
- }
- }
- // inject usb devices
- if !input.QemuArch.IsX86() {
- input.Devices = append(input.Devices,
- fmt.Sprintf("usb-tablet,id=input0,bus=%s.0,port=1", s.Desc.Usb.Id),
- fmt.Sprintf("usb-kbd,id=input1,bus=%s.0,port=2", s.Desc.Usb.Id),
- )
- // "qemu-xhci,p2=8,p3=8,id=usb1",
- // "usb-tablet,id=input0,bus=usb1.0,port=1",
- // "usb-kbd,id=input1,bus=usb1.0,port=2",
- // "virtio-gpu-pci,id=video1,max_outputs=1",
- } else {
- if !utils.IsInStringArray(s.getOsDistribution(), []string{OS_NAME_OPENWRT, OS_NAME_CIRROS}) &&
- !s.IsOldWindows() && !s.isWindows10() &&
- !s.disableUsbKbd() {
- input.Devices = append(input.Devices, "usb-kbd")
- }
- if input.OsName == OS_NAME_ANDROID {
- input.Devices = append(input.Devices, "usb-mouse")
- } else if !s.IsOldWindows() {
- input.Devices = append(input.Devices, "usb-tablet")
- }
- }
- // inject spice and vnc display
- input.IsVdiSpice = s.IsVdiSpice()
- input.SpicePort = uint(5900 + vncPort)
- input.VNCPassword = options.HostOptions.SetVncPassword
- input.IsKVMSupport = s.IsKvmSupport()
- input.ExtraOptions = append(input.ExtraOptions, s.extraOptions())
- if jsonutils.QueryBoolean(data, "need_migrate", false) {
- input.NeedMigrate = true
- input.LiveMigratePort = uint(*s.LiveMigrateDestPort)
- if jsonutils.QueryBoolean(data, "live_migrate_use_tls", false) {
- s.LiveMigrateUseTls = true
- input.LiveMigrateUseTLS = true
- } else {
- s.LiveMigrateUseTls = false
- }
- } else if s.Desc.IsSlave {
- log.Infof("backup guest with dest port %v", s.LiveMigrateDestPort)
- input.LiveMigratePort = uint(*s.LiveMigrateDestPort)
- }
- if s.Desc.IsSlave && !jsonutils.QueryBoolean(data, "block_ready", false) {
- diskUri, err := data.GetString("disk_uri")
- if err != nil {
- return "", errors.Wrap(err, "guest start missing disk uri")
- }
- if err := s.slaveDiskPrepare(input, diskUri); err != nil {
- return "", err
- }
- }
- // set rescue flag to input
- if s.Desc.LightMode {
- input.RescueInitrdPath = s.getRescueInitrdPath()
- input.RescueKernelPath = s.getRescueKernelPath()
- }
- // check if kickstart is needed for KVM guests
- if err := s.configureKickstartBoot(input); err != nil {
- return "", errors.Wrap(err, "handle kickstart mount")
- }
- qemuOpts, err := qemu.GenerateStartOptions(input)
- if err != nil {
- return "", errors.Wrap(err, "GenerateStartCommand")
- }
- cmd = fmt.Sprintf("%s %s", cmd, qemuOpts)
- cmd += "\"\n\n"
- cmd += "echo $CMD\n"
- return cmd, nil
- }
- // shouldUseKickstart 判断是否需要启用kickstart自动化安装
- // 启动kickstart的条件:1. 虚拟机处于KVM虚拟化环境;2. 存在kickstart配置且未禁用;3. kickstart未完成
- func (s *SKVMGuestInstance) shouldUseKickstart() bool {
- // 只在KVM虚拟化环境下处理kickstart
- if s.Desc.Hypervisor != api.HYPERVISOR_KVM {
- return false
- }
- kickstartCompleted, completedExists := s.Desc.Metadata[api.VM_METADATA_KICKSTART_COMPLETED_FLAG]
- if completedExists && kickstartCompleted == "true" {
- log.Debugf("Kickstart already completed for VM %s, skipping kickstart boot", s.Id)
- return false
- }
- // 检查是否存在kickstart配置
- kickstartConfigStr, configExists := s.Desc.Metadata[api.VM_METADATA_KICKSTART_CONFIG]
- if !configExists || kickstartConfigStr == "" {
- return false
- } else {
- kickstartConfigJson, err := jsonutils.ParseString(kickstartConfigStr)
- if err != nil {
- log.Errorf("Failed to parse kickstart config for VM %s: %v", s.Id, err)
- return false
- }
- kickstartConfig := &api.KickstartConfig{}
- if err := kickstartConfigJson.Unmarshal(kickstartConfig); err != nil {
- log.Errorf("Failed to unmarshal kickstart config for VM %s: %v", s.Id, err)
- return false
- }
- if kickstartConfig.Enabled != nil && !*kickstartConfig.Enabled {
- log.Debugf("Kickstart is disabled in config for VM %s, skipping kickstart boot", s.Id)
- return false
- }
- }
- log.Debugf("VM %s needs kickstart: config exists and not completed yet", s.Id)
- return true
- }
- // configureKickstartBoot 配置 kickstart 自动化安装的启动流程
- // 1. 挂载安装 ISO,获取内核和 initrd 路径
- // 2. 生成内核启动参数
- // 3. 创建 kickstart 监控器
- // 4. 如果包含完整配置,生成 kickstart 配置 ISO 并添加为 CDROM 设备
- func (s *SKVMGuestInstance) configureKickstartBoot(input *qemu.GenerateStartOptionsInput) error {
- if !s.shouldUseKickstart() {
- return nil
- }
- log.Debugf("Enabling kickstart boot for VM %s", s.Id)
- kickstartConfigStr := s.Desc.Metadata[api.VM_METADATA_KICKSTART_CONFIG]
- kickstartConfigJson, err := jsonutils.ParseString(kickstartConfigStr)
- if err != nil {
- return errors.Wrap(err, "parse kickstart config")
- }
- kickstartConfig := &api.KickstartConfig{}
- if err := kickstartConfigJson.Unmarshal(kickstartConfig); err != nil {
- return errors.Wrap(err, "unmarshal kickstart config")
- }
- // Find ISO file for kickstart installation from CDROM devices
- var isoPath string
- if len(s.Desc.Cdroms) > 0 {
- for _, cdrom := range s.Desc.Cdroms {
- if cdrom.Path != "" {
- isoPath = cdrom.Path
- break
- }
- }
- }
- if isoPath == "" {
- log.Warningf("no ISO path found for kickstart, skip")
- return nil
- }
- kickstartDir := s.getKickstartTmpDir()
- mountPoint := filepath.Join(kickstartDir, KICKSTART_ISO_MOUNT_DIR)
- // Check if mount point already exists and is mounted
- if fileutils2.Exists(mountPoint) {
- mountFile := "/proc/mounts"
- if data, err := os.ReadFile(mountFile); err == nil {
- lines := strings.Split(string(data), "\n")
- mounted := false
- for _, line := range lines {
- parts := strings.Split(line, " ")
- if len(parts) >= 2 && parts[1] == mountPoint {
- mounted = true
- break
- }
- }
- if mounted {
- log.Debugf("Reusing existing kickstart ISO mount at %s for guest %s", mountPoint, s.GetName())
- } else {
- os.RemoveAll(mountPoint)
- if err := os.MkdirAll(mountPoint, 0755); err != nil {
- return errors.Wrap(err, "create mount point")
- }
- if err := mountutils.MountWithParams(isoPath, mountPoint, "iso9660", []string{"-o", "loop,ro"}); err != nil {
- os.RemoveAll(mountPoint)
- return errors.Wrapf(err, "mount ISO %s to %s", isoPath, mountPoint)
- }
- log.Debugf("Successfully mounted kickstart ISO %s to %s for guest %s", isoPath, mountPoint, s.GetName())
- }
- }
- } else {
- if err := os.MkdirAll(mountPoint, 0755); err != nil {
- return errors.Wrap(err, "create mount point")
- }
- if err := mountutils.MountWithParams(isoPath, mountPoint, "iso9660", []string{"-o", "loop,ro"}); err != nil {
- os.RemoveAll(mountPoint)
- return errors.Wrapf(err, "mount ISO %s to %s", isoPath, mountPoint)
- }
- log.Debugf("Successfully mounted kickstart ISO %s to %s for guest %s", isoPath, mountPoint, s.GetName())
- }
- mountPath := mountPoint
- // get kernel and initrd path from mounted ISO
- kernelPath, initrdPath, err := GetKernelInitrdPaths(mountPath, kickstartConfig.OSType)
- if err != nil {
- return errors.Wrap(err, "get kickstart kernel paths")
- }
- // Copy kernel and initrd files to local directory and unmount ISO
- kernelCopyDir := filepath.Join(kickstartDir, "bootfiles")
- if err := os.MkdirAll(kernelCopyDir, 0755); err != nil {
- return errors.Wrap(err, "create kernel copy directory")
- }
- kernelFileName := filepath.Base(kernelPath)
- initrdFileName := filepath.Base(initrdPath)
- copiedKernelPath := filepath.Join(kernelCopyDir, kernelFileName)
- copiedInitrdPath := filepath.Join(kernelCopyDir, initrdFileName)
- if err := procutils.NewCommand("cp", kernelPath, copiedKernelPath).Run(); err != nil {
- return errors.Wrapf(err, "copy kernel from %s to %s", kernelPath, copiedKernelPath)
- }
- log.Debugf("Successfully copied kernel from %s to %s for guest %s", kernelPath, copiedKernelPath, s.GetName())
- if err := procutils.NewCommand("cp", initrdPath, copiedInitrdPath).Run(); err != nil {
- return errors.Wrapf(err, "copy initrd from %s to %s", initrdPath, copiedInitrdPath)
- }
- log.Debugf("Successfully copied initrd from %s to %s for guest %s", initrdPath, copiedInitrdPath, s.GetName())
- // Unmount ISO after copying files
- log.Infof("Unmounting kickstart ISO at %s after copying kernel and initrd for guest %s", mountPoint, s.GetName())
- if err := mountutils.Unmount(mountPoint, true); err != nil {
- log.Warningf("Failed to unmount kickstart ISO at %s: %v", mountPoint, err)
- } else {
- log.Debugf("Successfully unmounted kickstart ISO at %s for guest %s", mountPoint, s.GetName())
- // Remove mount point directory after unmounting
- if err := os.RemoveAll(mountPoint); err != nil {
- log.Warningf("Failed to remove mount point directory %s: %v", mountPoint, err)
- }
- }
- // Use copied file paths instead of mounted paths
- kernelPath = copiedKernelPath
- initrdPath = copiedInitrdPath
- var kickstartConfigIsoPath string
- log.Debugf("Kickstart config for guest %s: Config length=%d, ConfigURL=%s",
- s.GetName(), len(kickstartConfig.Config), kickstartConfig.ConfigURL)
- isoPath, err = CreateKickstartConfigISO(kickstartConfig, s.getKickstartTmpDir())
- if err != nil {
- log.Errorf("Failed to create kickstart config ISO for guest %s: %v, falling back to URL/cdrom method", s.GetName(), err)
- } else {
- kickstartConfigIsoPath = isoPath
- log.Debugf("Successfully created kickstart ISO for guest %s: %s", s.GetName(), isoPath)
- }
- kernelArgs := BuildKickstartAppendArgs(kickstartConfig, kickstartConfigIsoPath)
- log.Debugf("Generated kickstart kernel args for guest %s: %s", s.GetName(), kernelArgs)
- // Create kickstart serial monitor for status monitoring
- kickstartMonitor := NewKickstartSerialMonitor(s)
- serialFilePath := kickstartMonitor.GetSerialFilePath()
- input.KickstartBoot = &qemu.KickstartBootInfo{
- Config: kickstartConfig,
- MountPath: mountPath,
- KernelPath: kernelPath,
- InitrdPath: initrdPath,
- KernelArgs: kernelArgs,
- SerialFilePath: serialFilePath,
- ConfigIsoPath: kickstartConfigIsoPath,
- }
- // Add kickstart config ISO as additional CDROM device if created
- if kickstartConfigIsoPath != "" {
- if err := s.attachKickstartISO(kickstartConfigIsoPath); err != nil {
- log.Warningf("Failed to attach kickstart config ISO %s: %v", kickstartConfigIsoPath, err)
- }
- }
- s.kickstartMonitor = kickstartMonitor
- log.Debugf("Kickstart boot configured for guest %s: kernel=%s, initrd=%s, args=%s, isoPath=%s",
- s.GetName(), kernelPath, initrdPath, kernelArgs, kickstartConfigIsoPath)
- return nil
- }
- func (s *SKVMGuestInstance) getRescueInitrdPath() string {
- return path.Join(s.GetRescueDirPath(), api.GUEST_RESCUE_INITRAMFS)
- }
- func (s *SKVMGuestInstance) getRescueKernelPath() string {
- return path.Join(s.GetRescueDirPath(), api.GUEST_RESCUE_KERNEL)
- }
- func (s *SKVMGuestInstance) slaveDiskPrepare(input *qemu.GenerateStartOptionsInput, diskUri string) error {
- for i := 0; i < len(input.GuestDesc.Disks); i++ {
- diskPath := input.GuestDesc.Disks[i].Path
- d, err := storageman.GetManager().GetDiskByPath(diskPath)
- if err != nil {
- return errors.Wrapf(err, "GetDiskByPath(%s)", diskPath)
- }
- err = d.RebuildSlaveDisk(diskUri)
- if err != nil {
- return errors.Wrap(err, "RebuildSlaveDisk")
- }
- }
- return nil
- }
- func (s *SKVMGuestInstance) parseCmdline(input string) (*qemutils.Cmdline, []qemutils.Option, error) {
- cl, err := qemutils.NewCmdline(input)
- if err != nil {
- return nil, nil, errors.Wrapf(err, "NewCmdline %q", input)
- }
- filterOpts := make([]qemutils.Option, 0)
- // filter migrate and other option include dynamic port
- cl.FilterOption(func(o qemutils.Option) bool {
- switch o.Key {
- case "incoming":
- if strings.HasPrefix(o.Value, "tcp:") || strings.HasPrefix(o.Value, "defer") {
- filterOpts = append(filterOpts, o)
- return true
- }
- case "vnc", "spice", "daemonize":
- filterOpts = append(filterOpts, o)
- return true
- case "chardev":
- valsMatch := []string{
- "socket,id=hmqmondev",
- "socket,id=hmpmondev",
- "socket,id=qmqmondev",
- "socket,id=qmpmondev",
- }
- for _, valM := range valsMatch {
- if strings.HasPrefix(o.Value, valM) {
- filterOpts = append(filterOpts, o)
- return true
- }
- }
- }
- return false
- })
- return cl, filterOpts, nil
- }
- func (s *SKVMGuestInstance) _unifyMigrateQemuCmdline(cur string, src string) string {
- dmp := diffmatchpatch.New()
- diffs := dmp.DiffMain(cur, src, false)
- log.Debugf("unify migrate qemu cmdline diffs: %s", jsonutils.Marshal(diffs).PrettyString())
- // make patch
- patch := dmp.PatchMake(cur, diffs)
- // apply patch
- newStr, _ := dmp.PatchApply(patch, cur)
- return newStr
- }
- func (s *SKVMGuestInstance) unifyMigrateQemuCmdline(cur string, src string) (string, error) {
- curCl, curFilterOpts, err := s.parseCmdline(cur)
- if err != nil {
- return "", errors.Wrapf(err, "parseCmdline current %q", cur)
- }
- srcCl, _, err := s.parseCmdline(src)
- if err != nil {
- return "", errors.Wrapf(err, "parseCmdline source %q", src)
- }
- unifyStr := s._unifyMigrateQemuCmdline(curCl.ToString(), srcCl.ToString())
- unifyCl, _, err := s.parseCmdline(unifyStr)
- if err != nil {
- return "", errors.Wrapf(err, "parseCmdline unitfy %q", unifyStr)
- }
- unifyCl.AddOption(curFilterOpts...)
- return unifyCl.ToString(), nil
- }
- func (s *SKVMGuestInstance) generateStopScript(data *jsonutils.JSONDict) string {
- var (
- uuid = s.Desc.Uuid
- nics = s.Desc.Nics
- )
- cmd := ""
- cmd += fmt.Sprintf("VNC_FILE=%s\n", s.GetVncFilePath())
- cmd += fmt.Sprintf("PID_FILE=%s\n", s.GetPidFilePath())
- cmd += "if [ \"$1\" != \"--force\" ] && [ -f $VNC_FILE ]; then\n"
- cmd += " VNC=`cat $VNC_FILE`\n"
- // TODO, replace with qmp monitor
- cmd += fmt.Sprintf(" MON=$(($VNC + %d))\n", MONITOR_PORT_BASE)
- cmd += " echo quit | nc -w 1 127.0.0.1 $MON > /dev/null\n"
- cmd += " sleep 1\n"
- cmd += " echo \"Remove VNC $VNC_FILE\"\n"
- cmd += " rm -f $VNC_FILE\n"
- cmd += "fi\n"
- cmd += "if [ -f $PID_FILE ]; then\n"
- cmd += " PID=`cat $PID_FILE`\n"
- cmd += " ps -p $PID > /dev/null\n"
- cmd += " if [ $? -eq 0 ]; then\n"
- cmd += " echo \"Kill process $PID\"\n"
- cmd += " kill -9 $PID > /dev/null 2>&1\n"
- cmd += " fi\n"
- cmd += " echo \"Remove PID $PID_FILE\"\n"
- cmd += " rm -f $PID_FILE\n"
- cmd += "fi\n"
- cmd += fmt.Sprintf("for d in $(ls -d /dev/hugepages/%s*)\n", uuid)
- cmd += "do\n"
- cmd += " if [ -d $d ]; then\n"
- cmd += " umount $d\n"
- cmd += " rm -rf $d\n"
- cmd += " fi\n"
- cmd += "done\n"
- if s.enableTpmDev() {
- // stop swtpm
- cmd += fmt.Sprintf("SWTPM_PID_FILE=%s\n", s.getSwtpmPidPath())
- cmd += "if [ -f $SWTPM_PID_FILE ]; then\n"
- cmd += " SWTPM_PID=`cat $SWTPM_PID_FILE`\n"
- cmd += " ps -p $SWTPM_PID > /dev/null\n"
- cmd += " if [ $? -eq 0 ]; then\n"
- cmd += " echo \"Kill swtpm process $SWTPM_PID\"\n"
- cmd += " kill -9 $SWTPM_PID > /dev/null 2>&1\n"
- cmd += " fi\n"
- cmd += " echo \"Remove swtpm PID $SWTPM_PID_FILE\"\n"
- cmd += " rm -f $SWTPM_PID_FILE\n"
- cmd += "fi\n"
- }
- for _, nic := range nics {
- if nic.Driver == api.NETWORK_DRIVER_VFIO {
- dev, _ := s.GetSriovDeviceByNetworkIndex(nic.Index)
- if dev != nil && dev.GetOvsOffloadInterfaceName() == "" {
- continue
- }
- }
- downscript := s.getNicDownScriptPath(nic)
- cmd += fmt.Sprintf("%s %s\n", downscript, nic.Ifname)
- }
- return cmd
- }
- func (s *SKVMGuestInstance) presendArpForNic(nic *desc.SGuestNetwork) {
- ifi, err := net.InterfaceByName(nic.Ifname)
- if err != nil {
- log.Errorf("InterfaceByName error %s", nic.Ifname)
- return
- }
- cli, err := arp.Dial(ifi)
- if err != nil {
- log.Errorf("arp Dial error %s", err)
- return
- }
- defer cli.Close()
- var (
- sSrcMac = nic.Mac
- sScrIp = nic.Ip
- srcIp = net.ParseIP(sScrIp)
- dstMac, _ = net.ParseMAC("00:00:00:00:00:00")
- dstIp = net.ParseIP("255.255.255.255")
- )
- srcMac, err := net.ParseMAC(sSrcMac)
- if err != nil {
- log.Errorf("Send arp parse mac error: %s", err)
- return
- }
- pkt, err := arp.NewPacket(arp.OperationRequest, srcMac, srcIp, dstMac, dstIp)
- if err != nil {
- log.Errorf("New arp packet error %s", err)
- return
- }
- if err := cli.WriteTo(pkt, ethernet.Broadcast); err != nil {
- log.Errorf("Send arp packet error %s ", err)
- return
- }
- }
- func (s *SKVMGuestInstance) StartPresendArp() {
- go func() {
- for i := 0; i < 5; i++ {
- for _, nic := range s.Desc.Nics {
- s.presendArpForNic(nic)
- }
- time.Sleep(1 * time.Second)
- }
- }()
- }
- func (s *SKVMGuestInstance) getPKIDirPath() string {
- return path.Join(s.HomeDir(), "pki")
- }
- func (s *SKVMGuestInstance) getSwtpmDirPath() string {
- return path.Join(s.HomeDir(), "swtpm")
- }
- func (s *SKVMGuestInstance) getSwtpmSocketPath() string {
- return path.Join(s.getSwtpmDirPath(), "swtpm.sock")
- }
- func (s *SKVMGuestInstance) getSwtpmLogPath() string {
- return path.Join(s.getSwtpmDirPath(), "swtpm.log")
- }
- func (s *SKVMGuestInstance) getSwtpmPidPath() string {
- return path.Join(s.getSwtpmDirPath(), "swtpm.pid")
- }
- func (s *SKVMGuestInstance) makePKIDir() error {
- output, err := procutils.NewCommand("mkdir", "-p", s.getPKIDirPath()).Output()
- if err != nil {
- return errors.Wrapf(err, "mkdir %s failed: %s", s.getPKIDirPath(), output)
- }
- return nil
- }
- func (s *SKVMGuestInstance) PrepareMigrateCerts() (map[string]string, error) {
- pkiDir := s.getPKIDirPath()
- if err := s.makePKIDir(); err != nil {
- return nil, errors.Wrap(err, "make pki dir")
- }
- tree, err := qemucerts.GetDefaultCertList().AsMap().CertTree()
- if err != nil {
- return nil, errors.Wrap(err, "construct cert tree")
- }
- if err := tree.CreateTree(pkiDir); err != nil {
- return nil, errors.Wrap(err, "create certs")
- }
- return qemucerts.FetchDefaultCerts(pkiDir)
- }
- func (s *SKVMGuestInstance) WriteMigrateCerts(certs map[string]string) error {
- pkiDir := s.getPKIDirPath()
- if err := s.makePKIDir(); err != nil {
- return errors.Wrap(err, "make pki dir")
- }
- if err := qemucerts.CreateByMap(pkiDir, certs); err != nil {
- return errors.Wrapf(err, "create by map %#v", certs)
- }
- return nil
- }
- func (s *SKVMGuestInstance) SetNicDown(mac string) error {
- var nic *desc.SGuestNetwork
- for i := range s.Desc.Nics {
- if s.Desc.Nics[i].Mac == mac {
- nic = s.Desc.Nics[i]
- break
- }
- }
- if nic == nil {
- return errors.Errorf("guest %s has no nic with mac %s", s.GetName(), mac)
- }
- scriptPath := s.getNicDownScriptPath(nic)
- out, err := procutils.NewRemoteCommandAsFarAsPossible("bash", scriptPath).Output()
- if err != nil {
- return errors.Wrapf(err, "failed run nic down script %s", out)
- }
- return nil
- }
- func (s *SKVMGuestInstance) SetNicUp(nic *desc.SGuestNetwork) error {
- scriptPath := s.getNicUpScriptPath(nic)
- out, err := procutils.NewRemoteCommandAsFarAsPossible("bash", scriptPath).Output()
- if err != nil {
- return errors.Wrapf(err, "failed run nic down script %s", out)
- }
- return nil
- }
- func (s *SKVMGuestInstance) startMemCleaner() error {
- err := procutils.NewRemoteCommandAsFarAsPossible(
- options.HostOptions.BinaryMemcleanPath,
- "--pid", strconv.Itoa(s.GetPid()),
- "--mem-size", strconv.FormatInt(s.Desc.Mem*1024*1024, 10),
- "--log-dir", s.HomeDir(),
- ).Run()
- if err != nil {
- log.Errorf("failed start memcleaner: %s", err)
- return errors.Wrap(err, "start memclean")
- }
- return nil
- }
- func (s *SKVMGuestInstance) gpusHasVga() bool {
- manager := s.manager.GetHost().GetIsolatedDeviceManager()
- for i := 0; i < len(s.Desc.IsolatedDevices); i++ {
- dev := manager.GetDeviceByAddr(s.Desc.IsolatedDevices[i].Addr)
- if dev.GetDeviceType() == api.GPU_VGA_TYPE {
- return true
- }
- }
- return false
- }
- func (s *SKVMGuestInstance) hasGPU() bool {
- manager := s.manager.GetHost().GetIsolatedDeviceManager()
- for i := 0; i < len(s.Desc.IsolatedDevices); i++ {
- dev := manager.GetDeviceByAddr(s.Desc.IsolatedDevices[i].Addr)
- if dev.GetDeviceType() == api.GPU_VGA_TYPE || dev.GetDeviceType() == api.GPU_HPC_TYPE {
- return true
- }
- }
- return false
- }
- func (s *SKVMGuestInstance) initCpuDesc(cpuMax uint) error {
- var err error
- s.fixGuestMachineType()
- if cpuMax == 0 {
- cpuMax, err = s.CpuMax()
- if err != nil {
- return err
- }
- }
- cpuDesc, err := s.archMan.GenerateCpuDesc(uint(s.Desc.Cpu), cpuMax, s)
- if err != nil {
- return err
- }
- s.Desc.CpuDesc = cpuDesc
- // if region not allocate cpu numa pin
- if len(s.Desc.CpuNumaPin) == 0 {
- err = s.allocGuestNumaCpuset()
- if err != nil {
- return err
- }
- }
- return nil
- }
- func (s *SKVMGuestInstance) initMemDesc(memSizeMB int64) error {
- s.Desc.MemDesc = s.archMan.GenerateMemDesc()
- s.Desc.MemDesc.SizeMB = memSizeMB
- return s.initGuestMemObjects(memSizeMB)
- }
- func (s *SKVMGuestInstance) memObjectType() string {
- if s.manager.host.IsHugepagesEnabled() {
- return "memory-backend-file"
- } else if s.isMemcleanEnabled() {
- return "memory-backend-memfd"
- } else {
- return "memory-backend-ram"
- }
- }
- func (s *SKVMGuestInstance) initGuestMemObjects(memSizeMB int64) error {
- if len(s.Desc.CpuNumaPin) == 0 {
- s.initDefaultMemObject(memSizeMB)
- return nil
- }
- var numaMems int64
- var numaCpus = int(s.Desc.CpuDesc.MaxCpus) / len(s.Desc.CpuNumaPin)
- var leastCpus = int(s.Desc.CpuDesc.MaxCpus) % len(s.Desc.CpuNumaPin)
- var cpuStart = 0
- var cpuEnd = numaCpus - 1
- var mems = make([]desc.SMemDesc, 0)
- for i := 0; i < len(s.Desc.CpuNumaPin); i++ {
- if s.Desc.CpuNumaPin[i].SizeMB <= 0 {
- continue
- }
- numaMems += s.Desc.CpuNumaPin[i].SizeMB
- memId := "mem"
- nodeId := uint16(i)
- if i > 0 {
- memId += strconv.Itoa(i - 1)
- }
- if i == 0 {
- cpuEnd += leastCpus
- }
- vcpus := fmt.Sprintf("%d-%d", cpuStart, cpuEnd)
- for j := range s.Desc.CpuNumaPin[i].VcpuPin {
- s.Desc.CpuNumaPin[i].VcpuPin[j].Vcpu = cpuStart + j
- }
- cpuStart = cpuEnd + 1
- cpuEnd = cpuStart + numaCpus - 1
- if s.Desc.CpuNumaPin[i].Unregular {
- continue
- }
- memDesc := desc.NewMemDesc(s.memObjectType(), memId, &nodeId, &vcpus)
- memDesc.Options = s.getMemObjectOptions(s.Desc.CpuNumaPin[i].SizeMB, s.Desc.Uuid, s.Desc.CpuNumaPin[i].NodeId)
- mems = append(mems, *memDesc)
- }
- if len(mems) == 0 {
- // numa mems not regular
- s.initDefaultMemObject(memSizeMB)
- return nil
- }
- s.Desc.MemDesc.Mem = desc.NewMemsDesc(mems[0], mems[1:])
- return nil
- }
- func (s *SKVMGuestInstance) getMemObjectOptions(memSizeMB int64, memPathSuffix string, hostNodes *uint16) map[string]string {
- var opts map[string]string
- if s.manager.host.IsHugepagesEnabled() {
- opts = map[string]string{
- "mem-path": fmt.Sprintf("/dev/hugepages/%s", memPathSuffix),
- "size": fmt.Sprintf("%dM", memSizeMB),
- "share": "on", "prealloc": "on",
- }
- if hostNodes != nil {
- opts["host-nodes"] = fmt.Sprintf("%d", *hostNodes)
- opts["policy"] = "bind"
- }
- } else if s.isMemcleanEnabled() {
- opts = map[string]string{
- "size": fmt.Sprintf("%dM", memSizeMB),
- "share": "on", "prealloc": "on",
- }
- } else {
- opts = map[string]string{
- "size": fmt.Sprintf("%dM", memSizeMB),
- }
- }
- return opts
- }
- func (s *SKVMGuestInstance) initDefaultMemObject(memSizeMB int64) {
- defaultDesc := desc.NewMemDesc(s.memObjectType(), "mem", nil, nil)
- defaultDesc.Options = s.getMemObjectOptions(memSizeMB, s.Desc.Uuid, nil)
- s.Desc.MemDesc.Mem = desc.NewMemsDesc(*defaultDesc, nil)
- }
- func (s *SKVMGuestInstance) defaultMemNodeHasObject(memDevs []monitor.Memdev) bool {
- for _, dev := range memDevs {
- if dev.ID != nil && *dev.ID == "mem" {
- return true
- }
- }
- return false
- }
- func (s *SKVMGuestInstance) initMemDescFromMemoryInfo(
- memoryDevicesInfoList []monitor.MemoryDeviceInfo, memDevs []monitor.Memdev,
- ) error {
- memSize := s.Desc.Mem
- memSlots := make([]*desc.SMemSlot, 0)
- for i := 0; i < len(memoryDevicesInfoList); i++ {
- if memoryDevicesInfoList[i].Type != "dimm" || memoryDevicesInfoList[i].Data.ID == nil {
- return errors.Errorf("unsupported memory device type %s", memoryDevicesInfoList[i].Type)
- }
- memSize -= (memoryDevicesInfoList[i].Data.Size / 1024 / 1024)
- memObj := desc.NewMemDesc(s.memObjectType(), path.Base(memoryDevicesInfoList[i].Data.Memdev), nil, nil)
- memObj.Options = map[string]string{
- "size": fmt.Sprintf("%dM", memoryDevicesInfoList[i].Data.Size/1024/1024),
- }
- memSlots = append(memSlots, &desc.SMemSlot{
- SizeMB: memoryDevicesInfoList[i].Data.Size / 1024 / 1024,
- MemObj: memObj,
- MemDev: &desc.SMemDevice{
- Type: "pc-dimm", Id: *memoryDevicesInfoList[i].Data.ID,
- },
- })
- }
- if memSize <= 0 {
- return errors.Errorf("wrong memsize %d", s.Desc.Mem)
- }
- s.Desc.MemDesc = s.archMan.GenerateMemDesc()
- s.Desc.MemDesc.SizeMB = memSize
- if s.defaultMemNodeHasObject(memDevs) {
- s.initDefaultMemObject(memSize)
- }
- s.Desc.MemDesc.MemSlots = memSlots
- return nil
- }
- func (s *SKVMGuestInstance) fixGuestMachineType() {
- if s.GetOsName() == OS_NAME_MACOS {
- s.Desc.Machine = api.VM_MACHINE_TYPE_Q35
- s.Desc.Bios = qemu.BIOS_UEFI
- }
- if !s.manager.host.IsX8664() {
- if utils.IsInStringArray(s.Desc.Machine, []string{
- "", api.VM_MACHINE_TYPE_PC, api.VM_MACHINE_TYPE_Q35,
- }) {
- s.Desc.Machine = api.VM_MACHINE_TYPE_VIRT
- }
- }
- if s.manager.host.IsAarch64() || s.manager.host.IsRiscv64() {
- s.Desc.Bios = qemu.BIOS_UEFI
- }
- }
- func (s *SKVMGuestInstance) initMachineDesc() {
- if s.Desc.Machine == "" {
- s.Desc.Machine = s.getMachine()
- }
- s.Desc.MachineDesc = s.archMan.GenerateMachineDesc(s.Desc.CpuDesc.Accel)
- if options.HostOptions.NoHpet {
- noHpet := true
- s.Desc.NoHpet = &noHpet
- }
- }
- func (s *SKVMGuestInstance) initQgaDesc() {
- s.Desc.Qga = s.archMan.GenerateQgaDesc(path.Join(s.HomeDir(), "qga.sock"))
- }
- func (s *SKVMGuestInstance) initPvpanicDesc() {
- if !s.disablePvpanicDev() {
- s.Desc.Pvpanic = s.archMan.GeneratePvpanicDesc()
- }
- }
- func (s *SKVMGuestInstance) initIsaSerialDesc() {
- if !s.disableIsaSerialDev() {
- s.Desc.IsaSerial = s.archMan.GenerateIsaSerialDesc()
- }
- }
- func (s *SKVMGuestInstance) initTpmDesc() {
- if s.enableTpmDev() {
- charDevId := "chrtpm"
- s.Desc.Tpm = &desc.SGuestTpm{
- TpmSock: desc.NewCharDev("socket", charDevId, ""),
- Id: "tpm0",
- }
- s.Desc.Tpm.TpmSock.Options = map[string]string{
- "path": s.getSwtpmSocketPath(),
- }
- }
- }
- func (s *SKVMGuestInstance) getVfioDeviceHotPlugPciControllerType() *desc.PCI_CONTROLLER_TYPE {
- if s.Desc.Machine == api.VM_MACHINE_TYPE_Q35 || s.Desc.Machine == api.VM_MACHINE_TYPE_VIRT {
- _, _, found := s.findUnusedSlotForController(desc.CONTROLLER_TYPE_PCIE_ROOT_PORT, 0)
- if found {
- var contType desc.PCI_CONTROLLER_TYPE = desc.CONTROLLER_TYPE_PCIE_ROOT_PORT
- return &contType
- }
- return nil
- } else {
- return s.getHotPlugPciControllerType()
- }
- }
- func (s *SKVMGuestInstance) getHotPlugPciControllerType() *desc.PCI_CONTROLLER_TYPE {
- for i := 0; i < len(s.Desc.PCIControllers); i++ {
- switch s.Desc.PCIControllers[i].CType {
- case desc.CONTROLLER_TYPE_PCI_ROOT, desc.CONTROLLER_TYPE_PCI_BRIDGE:
- var contType = s.Desc.PCIControllers[i].CType
- return &contType
- }
- }
- return nil
- }
- func (s *SKVMGuestInstance) vfioDevCount() int {
- res := 0
- for i := 0; i < len(s.Desc.IsolatedDevices); i++ {
- if s.Desc.IsolatedDevices[i].DevType != api.USB_TYPE {
- res += 1
- }
- }
- return res
- }
- func (s *SKVMGuestInstance) getOvmfVarsPath() string {
- ovmfVarsName := filepath.Base(options.HostOptions.OvmfVarsPath)
- varsPath := path.Join(s.HomeDir(), ovmfVarsName)
- if fileutils2.Exists(varsPath) {
- return varsPath
- }
- ovmfVarsName = filepath.Base(options.HostOptions.SecbootOvmfVarsPath)
- varsPath = path.Join(s.HomeDir(), ovmfVarsName)
- if fileutils2.Exists(varsPath) {
- return varsPath
- }
- ovmfVarsName = filepath.Base(options.HostOptions.Ovmf4MCodeVarsPath)
- varsPath = path.Join(s.HomeDir(), ovmfVarsName)
- return varsPath
- }
- func (s *SKVMGuestInstance) getOvmfVarsSourcePath() (string, string) {
- ovmfVarsName := filepath.Base(options.HostOptions.OvmfVarsPath)
- varsPath := path.Join(s.HomeDir(), ovmfVarsName)
- if fileutils2.Exists(varsPath) {
- return options.HostOptions.OvmfPath, options.HostOptions.OvmfVarsPath
- }
- ovmfVarsName = filepath.Base(options.HostOptions.SecbootOvmfVarsPath)
- varsPath = path.Join(s.HomeDir(), ovmfVarsName)
- if fileutils2.Exists(varsPath) {
- return options.HostOptions.SecbootOvmfPath, options.HostOptions.SecbootOvmfVarsPath
- }
- if fileutils2.Exists(options.HostOptions.Ovmf4MCodeVarsPath) {
- return options.HostOptions.Ovmf4MCodePath, options.HostOptions.Ovmf4MCodeVarsPath
- } else {
- return options.HostOptions.OvmfPath, options.HostOptions.OvmfVarsPath
- }
- }
- func (s *SKVMGuestInstance) getDiskBootOrderType(driver string) uefi.OvmfDevicePathType {
- switch driver {
- case qemu.DISK_DRIVER_VIRTIO:
- return uefi.DEVICE_TYPE_PCI
- case qemu.DISK_DRIVER_SCSI, qemu.DISK_DRIVER_PVSCSI:
- return uefi.DEVICE_TYPE_SCSI
- case qemu.DISK_DRIVER_IDE:
- if !s.manager.host.IsX8664() {
- return uefi.DEVICE_TYPE_SCSI
- }
- return uefi.DEVICE_TYPE_IDE
- case qemu.DISK_DRIVER_SATA:
- if !s.manager.host.IsX8664() {
- return uefi.DEVICE_TYPE_SCSI
- }
- return uefi.DEVICE_TYPE_SATA
- }
- return uefi.DEVICE_TYPE_UNKNOWN
- }
- func (s *SKVMGuestInstance) getCdromBootOrder() uefi.OvmfDevicePathType {
- if !s.manager.host.IsX8664() {
- return uefi.DEVICE_TYPE_SCSI_CDROM
- }
- return uefi.DEVICE_TYPE_CDROM
- }
- func (s *SKVMGuestInstance) setUefiBootOrder(ctx context.Context) error {
- params := &deployapi.OvmfBootOrderParams{
- OvmfVarsPath: s.getOvmfVarsPath(),
- }
- devs := make([]*deployapi.BootDevices, 0)
- for i := range s.Desc.Cdroms {
- if s.Desc.Cdroms[i].BootIndex == nil || *s.Desc.Disks[i].BootIndex < 0 {
- continue
- }
- dev := &deployapi.BootDevices{
- BootOrder: int32(*s.Desc.Cdroms[i].BootIndex),
- AttachOrder: int32(s.Desc.Cdroms[i].Ordinal),
- DevType: int32(s.getCdromBootOrder()),
- }
- devs = append(devs, dev)
- }
- for i := range s.Desc.Disks {
- if s.Desc.Disks[i].BootIndex == nil || *s.Desc.Disks[i].BootIndex < 0 {
- continue
- }
- dev := &deployapi.BootDevices{
- BootOrder: int32(*s.Desc.Disks[i].BootIndex),
- AttachOrder: int32(s.Desc.Disks[i].Index),
- DevType: int32(s.getDiskBootOrderType(s.Desc.Disks[i].Driver)),
- }
- devs = append(devs, dev)
- }
- params.Devs = devs
- _, err := deployclient.GetDeployClient().SetOvmfBootOrder(ctx, params)
- if err != nil {
- return errors.Wrap(err, "SetOvmfBootOrder")
- }
- return nil
- }
- // attachKickstartISO attaches the kickstart ISO as an additional CDROM device
- // if the kickstart is not provided by URL
- func (s *SKVMGuestInstance) attachKickstartISO(isoPath string) error {
- cdromId := fmt.Sprintf("kickstart_iso_%s", s.Id)
- log.Debugf("Attaching kickstart ISO %s as CDROM device for guest %s", isoPath, s.GetName())
- kickstartCdrom := &desc.SGuestCdrom{
- Id: cdromId,
- Path: isoPath,
- Ordinal: int64(len(s.Desc.Cdroms)),
- Scsi: desc.NewScsiDevice("scsi.0", "scsi-cd", fmt.Sprintf("scsi-cd-%s", cdromId)),
- DriveOptions: map[string]string{
- "readonly": "on",
- "media": "cdrom",
- "if": "none",
- },
- }
- s.Desc.Cdroms = append(s.Desc.Cdroms, kickstartCdrom)
- log.Debugf("Successfully attached kickstart ISO %s as SCSI CDROM device %s (ordinal=%d) for guest %s",
- isoPath, cdromId, kickstartCdrom.Ordinal, s.GetName())
- return nil
- }
|