Libvirt 虚拟机创建和强制关机:代码解析
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())
}
代码分析
-
连接 Libvirt 服务器:
cli, err := libvirt.NewConnect('qemu:///system'):使用libvirt.NewConnect函数连接到 Libvirt 服务器,连接 URI 为qemu:///system。
-
创建虚拟机配置:
var req request.VirtualMachine:定义一个request.VirtualMachine结构体,用来存储虚拟机配置信息。- 代码中配置了虚拟机的名称、路径、系统类型、CPU 核心数、内存、磁盘、网络、启动类型、键盘语言、共享目录等信息。
-
创建虚拟机目录:
if _, err := os.Stat(req.Path); os.IsNotExist(err) { ... }:检查虚拟机路径是否存在,如果不存在则创建目录。
-
生成 Libvirt XML 配置:
var domain libvirtxml.Domain:定义一个libvirtxml.Domain结构体,用来存储虚拟机 XML 配置。- 代码中根据
req中的配置信息填充domain结构体,生成虚拟机 XML 配置。
-
创建虚拟机:
marshal, err := domain.Marshal():将domain结构体序列化为 XML 格式的字符串。creat, err := cli.DomainCreateXML(marshal, libvirt.DOMAIN_NONE):使用cli.DomainCreateXML函数根据 XML 配置创建虚拟机。
-
设置自动启动:
if req.Advanced.OtherConfig.AutoMaticStartUp { ... }:如果配置了自动启动,则使用creat.SetAutostart(true)设置虚拟机自动启动。
-
强制关机虚拟机:
err = creat.Destroy():使用creat.Destroy()方法强制关机虚拟机。
虚拟机删除问题
根据代码,使用 Destroy 方法只会强制关机虚拟机,而不会删除虚拟机。如果虚拟机被删除了,可能是以下原因:
- 代码中存在其他删除虚拟机的逻辑。
- 其他程序调用了删除虚拟机的代码。
建议检查代码中是否有其他与删除虚拟机相关的代码,以确定虚拟机被删除的原因。
注意:
- 代码中使用
utils.ShellExec执行qemu-img命令创建磁盘镜像,需要确保qemu-img命令在系统中已安装。 - 代码中使用了 Libvirt 的
Domain结构体和 XML 格式的配置,需要了解 Libvirt 的相关知识。 - 代码中使用了
request和libvirtxml包,需要确保这些包已安装。
希望以上分析对您理解 Libvirt 虚拟机创建和强制关机代码有所帮助。
原文地址: https://www.cveoy.top/t/topic/f3cT 著作权归作者所有。请勿转载和采集!