Libvirt 虚拟机创建和强制关机:代码解析

本文将分析使用 Libvirt 库创建虚拟机的代码,并解释使用 Destroy 方法强制关机虚拟机的原理。

cli, err := libvirt.NewConnect('qemu:///system')
	if err != nil {
		logger.Error(err.Error())
	}
	var req request.VirtualMachine
	req.VirtualMachineName = 'hulian'
	req.Path = '/home/wufan/testKvm'
	req.Resource.Images = []string{'/home/wufan/debian-11.6.0-amd64-DVD-1.iso'}
	req.SystemType = 'linux'
	req.Resource.Cores = 2
	req.Resource.Memory = 4
	req.Resource.Dists = []request.Dist{
		{
			Name: 'sata',
			Size: 4,
		},
		{
			Name: 'ide',
			Size: 2,
		},
	}
	req.Resource.Networks = []request.Network{
		{
			Type: 'e1000',
			Name: 'default',
		},
	}
	req.Advanced.Device.USBController = 2
	req.Advanced.Device.GraphicsCard = 'vga'
	req.Advanced.Device.BootType = 'bios'
	req.Advanced.OtherConfig.KeyboardLanguage = 'en-us'
	req.Advanced.OtherConfig.ShareDirectory = []request.ShareDirectory{
		{
			NasPath: '/home/wufan/testKvm/shared',
			VirPath: '/etc',
		},
	}
	if _, err := os.Stat(req.Path); os.IsNotExist(err) {
		logger.Error('the path is not exist')
	}
	join := filepath.Join(req.Path, req.VirtualMachineName)
	err = os.Mkdir(join, 0755)
	if err != nil {
		logger.Error(err.Error())
	}
	var domain libvirtxml.Domain
	domain.Type = 'qemu'
	domain.Name = req.VirtualMachineName
	//utils.ShellExec('qemu', '-img', 'create', '-f', 'qcow2')
	domain.OS = &libvirtxml.DomainOS{
		Type: &libvirtxml.DomainOSType{
			Type: 'hvm',
		},
	}
	if strings.Compare(req.Advanced.Device.BootType, 'uefi') == 0 {
		domain.OS.Loader = &libvirtxml.DomainLoader{
			Type:     'pflash',
			Readonly: 'yes',
			Path:     '/usr/share/OVMF/OVMF_CODE_4M.fd',
		}
	}
	if strings.Compare(req.SystemType, 'windows') == 0 {
		domain.Features = &libvirtxml.DomainFeatureList{}
		domain.Features.HyperV = &libvirtxml.DomainFeatureHyperV{}
		domain.Features.HyperV.Relaxed = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.VAPIC = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.Spinlocks = &libvirtxml.DomainFeatureHyperVSpinlocks{}
		domain.Features.HyperV.Spinlocks.DomainFeatureState = libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.VPIndex = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.Runtime = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.Synic = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.STimer = &libvirtxml.DomainFeatureHyperVSTimer{}
		domain.Features.HyperV.STimer.DomainFeatureState = libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.Reset = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.VendorId = &libvirtxml.DomainFeatureHyperVVendorId{}
		domain.Features.HyperV.VendorId.DomainFeatureState = libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.Frequencies = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.ReEnlightenment = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.TLBFlush = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.IPI = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
		domain.Features.HyperV.EVMCS = &libvirtxml.DomainFeatureState{
			State: 'on',
		}
	} else {
		domain.Features = &libvirtxml.DomainFeatureList{}
		domain.Features.HyperV = &libvirtxml.DomainFeatureHyperV{}
		domain.Features.HyperV.Relaxed = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.VAPIC = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.Spinlocks = &libvirtxml.DomainFeatureHyperVSpinlocks{}
		domain.Features.HyperV.Spinlocks.DomainFeatureState = libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.VPIndex = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.Runtime = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.Synic = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.STimer = &libvirtxml.DomainFeatureHyperVSTimer{}
		domain.Features.HyperV.STimer.DomainFeatureState = libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.Reset = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.VendorId = &libvirtxml.DomainFeatureHyperVVendorId{}
		domain.Features.HyperV.VendorId.DomainFeatureState = libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.Frequencies = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.ReEnlightenment = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.TLBFlush = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.IPI = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
		domain.Features.HyperV.EVMCS = &libvirtxml.DomainFeatureState{
			State: 'off',
		}
	}
	domain.VCPU = &libvirtxml.DomainVCPU{
		Value: uint(req.Cores),
	}
	domain.Memory = &libvirtxml.DomainMemory{
		Value: uint(req.Memory * 1024),
		Unit:  'MB',
	}
	var disks []libvirtxml.DomainDisk
	var networks []libvirtxml.DomainInterface
	i := 97
	for _, dist := range req.Dists {
		path := fmt.Sprintf('%s/%s', join, dist.Name+'.qcow2')
		mem := fmt.Sprintf('%dG', dist.Size)
		_, err = utils.ShellExec('qemu-img', 'create', '-f', 'qcow2', path, mem)
		if err != nil {
			logger.Error(err.Error())
		}
		disk := libvirtxml.DomainDisk{
			Device: 'disk',
			Driver: &libvirtxml.DomainDiskDriver{
				Name: 'qemu',
				Type: 'qcow2',
			},
			Source: &libvirtxml.DomainDiskSource{
				File: &libvirtxml.DomainDiskSourceFile{
					File: path,
				},
			},
			Target: &libvirtxml.DomainDiskTarget{
				Bus: dist.Name,
				Dev: fmt.Sprintf('vd%c', i),
			},
		}
		disks = append(disks, disk)
		i++
	}
	j := 97
	for _, image := range req.Images {
		disk := libvirtxml.DomainDisk{
			Device: 'cdrom',
			Driver: &libvirtxml.DomainDiskDriver{
				Name: 'qemu',
				Type: 'raw',
			},
			Source: &libvirtxml.DomainDiskSource{
				File: &libvirtxml.DomainDiskSourceFile{
					File: image,
				},
			},
			Target: &libvirtxml.DomainDiskTarget{
				Dev: fmt.Sprintf('sd%c', j),
			},
		}
		disks = append(disks, disk)
		j++
	}
	for _, network := range req.Networks {
		interfaces := libvirtxml.DomainInterface{
			Model: &libvirtxml.DomainInterfaceModel{
				Type: network.Type,
			},
			Source: &libvirtxml.DomainInterfaceSource{
				Network: &libvirtxml.DomainInterfaceSourceNetwork{
					Network: network.Name,
				},
			},
		}
		networks = append(networks, interfaces)
	}
	domain.Devices = &libvirtxml.DomainDeviceList{
		Interfaces: networks,
		Disks:      disks,
	}
	domain.Devices.Videos = []libvirtxml.DomainVideo{
		{
			Model: libvirtxml.DomainVideoModel{
				Type: req.Advanced.Device.GraphicsCard,
			},
		},
	}
	domain.Devices.Graphics = []libvirtxml.DomainGraphic{
		{
			VNC: &libvirtxml.DomainGraphicVNC{
				Keymap: req.Advanced.OtherConfig.KeyboardLanguage,
			},
		},
	}
	if req.Advanced.Device.USBController == 2 {
		domain.Devices.Controllers = []libvirtxml.DomainController{
			{
				Type:  'usb',
				Model: 'ich9-ehci1',
			},
		}
	} else if req.Advanced.Device.USBController == 3 {
		domain.Devices.Controllers = []libvirtxml.DomainController{
			{
				Type:  'usb',
				Model: 'qemu-xhci',
			},
		}
	}
	if len(req.Advanced.OtherConfig.ShareDirectory) > 0 {
		var shareDirectory []libvirtxml.DomainFilesystem
		for _, sharedPath := range req.Advanced.OtherConfig.ShareDirectory {
			fileSystem := libvirtxml.DomainFilesystem{
				Source: &libvirtxml.DomainFilesystemSource{
					Mount: &libvirtxml.DomainFilesystemSourceMount{
						Dir: sharedPath.NasPath,
					},
				},
				Target: &libvirtxml.DomainFilesystemTarget{
					Dir: sharedPath.VirPath,
				},
			}
			shareDirectory = append(shareDirectory, fileSystem)
		}
		domain.Devices.Filesystems = shareDirectory
	}
	marshal, err := domain.Marshal()
	if err != nil {
		logger.Error(err.Error())
	}
	creat, err := cli.DomainCreateXML(marshal, libvirt.DOMAIN_NONE)
	if err != nil {
		logger.Error(err.Error())
	}
	if req.Advanced.OtherConfig.AutoMaticStartUp {
		err = creat.SetAutostart(true)
		if err != nil {
			logger.Error(err.Error())
		}
	}
	err = creat.Destroy()
	if err != nil{
		logger.Error(err.Error())
	}

代码分析

  1. 连接 Libvirt 服务器:

    • cli, err := libvirt.NewConnect('qemu:///system'):使用 libvirt.NewConnect 函数连接到 Libvirt 服务器,连接 URI 为 qemu:///system
  2. 创建虚拟机配置:

    • var req request.VirtualMachine:定义一个 request.VirtualMachine 结构体,用来存储虚拟机配置信息。
    • 代码中配置了虚拟机的名称、路径、系统类型、CPU 核心数、内存、磁盘、网络、启动类型、键盘语言、共享目录等信息。
  3. 创建虚拟机目录:

    • if _, err := os.Stat(req.Path); os.IsNotExist(err) { ... }:检查虚拟机路径是否存在,如果不存在则创建目录。
  4. 生成 Libvirt XML 配置:

    • var domain libvirtxml.Domain:定义一个 libvirtxml.Domain 结构体,用来存储虚拟机 XML 配置。
    • 代码中根据 req 中的配置信息填充 domain 结构体,生成虚拟机 XML 配置。
  5. 创建虚拟机:

    • marshal, err := domain.Marshal():将 domain 结构体序列化为 XML 格式的字符串。
    • creat, err := cli.DomainCreateXML(marshal, libvirt.DOMAIN_NONE):使用 cli.DomainCreateXML 函数根据 XML 配置创建虚拟机。
  6. 设置自动启动:

    • if req.Advanced.OtherConfig.AutoMaticStartUp { ... }:如果配置了自动启动,则使用 creat.SetAutostart(true) 设置虚拟机自动启动。
  7. 强制关机虚拟机:

    • err = creat.Destroy():使用 creat.Destroy() 方法强制关机虚拟机。

虚拟机删除问题

根据代码,使用 Destroy 方法只会强制关机虚拟机,而不会删除虚拟机。如果虚拟机被删除了,可能是以下原因:

  1. 代码中存在其他删除虚拟机的逻辑。
  2. 其他程序调用了删除虚拟机的代码。

建议检查代码中是否有其他与删除虚拟机相关的代码,以确定虚拟机被删除的原因。

注意:

  • 代码中使用 utils.ShellExec 执行 qemu-img 命令创建磁盘镜像,需要确保 qemu-img 命令在系统中已安装。
  • 代码中使用了 Libvirt 的 Domain 结构体和 XML 格式的配置,需要了解 Libvirt 的相关知识。
  • 代码中使用了 requestlibvirtxml 包,需要确保这些包已安装。

希望以上分析对您理解 Libvirt 虚拟机创建和强制关机代码有所帮助。

Libvirt 虚拟机创建和强制关机:代码解析

原文地址: https://www.cveoy.top/t/topic/f3cT 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录