146 lines
4.3 KiB
Go
146 lines
4.3 KiB
Go
package libvirt
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
golibvirt "github.com/digitalocean/go-libvirt"
|
|
libvirtutils "github.com/thomasklein94/packer-plugin-libvirt/libvirt-utils"
|
|
)
|
|
|
|
type utils interface {
|
|
gatherAllDomains() (domains []golibvirt.Domain, err error)
|
|
gatherStatsForDomains(domains []golibvirt.Domain, metricNumber uint32) ([]golibvirt.DomainStatsRecord, error)
|
|
gatherNumberOfPCPUs() (int, error)
|
|
gatherVcpuMapping(domain golibvirt.Domain, pCPUs int, shouldGetCurrentPCPU bool) ([]vcpuAffinity, error)
|
|
ensureConnected(libvirtURI string) error
|
|
disconnect() error
|
|
}
|
|
|
|
type utilsImpl struct {
|
|
libvirt *golibvirt.Libvirt
|
|
}
|
|
|
|
type vcpuAffinity struct {
|
|
vcpuID string
|
|
coresAffinity string
|
|
currentPCPUID int32
|
|
}
|
|
|
|
// gatherAllDomains gathers all domains on system
|
|
func (l *utilsImpl) gatherAllDomains() (domains []golibvirt.Domain, err error) {
|
|
allDomainStatesFlag := golibvirt.ConnectListDomainsRunning + golibvirt.ConnectListDomainsPaused +
|
|
golibvirt.ConnectListDomainsShutoff + golibvirt.ConnectListDomainsOther
|
|
|
|
domains, _, err = l.libvirt.ConnectListAllDomains(1, allDomainStatesFlag)
|
|
return domains, err
|
|
}
|
|
|
|
// gatherStatsForDomains gathers stats for given domains based on number that was previously calculated
|
|
func (l *utilsImpl) gatherStatsForDomains(domains []golibvirt.Domain, metricNumber uint32) ([]golibvirt.DomainStatsRecord, error) {
|
|
if metricNumber == 0 {
|
|
// do not need to do expensive call if no stats were set to gather
|
|
return nil, nil
|
|
}
|
|
|
|
allDomainStatesFlag := golibvirt.ConnectGetAllDomainsStatsRunning + golibvirt.ConnectGetAllDomainsStatsPaused +
|
|
golibvirt.ConnectGetAllDomainsStatsShutoff + golibvirt.ConnectGetAllDomainsStatsOther
|
|
|
|
return l.libvirt.ConnectGetAllDomainStats(domains, metricNumber, uint32(allDomainStatesFlag))
|
|
}
|
|
|
|
func (l *utilsImpl) gatherNumberOfPCPUs() (int, error) {
|
|
//nolint:dogsled //Using only needed values from library function
|
|
_, _, _, _, nodes, sockets, cores, threads, err := l.libvirt.NodeGetInfo()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return int(nodes * sockets * cores * threads), nil
|
|
}
|
|
|
|
// gatherVcpuMapping is based on official go-libvirt library:
|
|
// https://github.com/libvirt/libvirt-go-module/blob/268a5d02e00cc9b3d5d7fa6c08d753071e7d14b8/domain.go#L4516
|
|
// (this library cannot be used here because of C bindings)
|
|
func (l *utilsImpl) gatherVcpuMapping(domain golibvirt.Domain, pCPUs int, shouldGetCurrentPCPU bool) ([]vcpuAffinity, error) {
|
|
//nolint:dogsled //Using only needed values from library function
|
|
_, _, _, vCPUs, _, err := l.libvirt.DomainGetInfo(domain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bytesToHoldPCPUs := (pCPUs + 7) / 8
|
|
|
|
cpuInfo, vcpuPinInfo, err := l.libvirt.DomainGetVcpus(domain, int32(vCPUs), int32(bytesToHoldPCPUs))
|
|
if err != nil {
|
|
// DomainGetVcpus gets not only affinity (1:N mapping from VCPU to PCPU)
|
|
// but also realtime 1:1 mapping from VCPU to PCPU
|
|
// Unfortunately it will return nothing (only error) for inactive domains -> for that case use
|
|
// DomainGetVcpuPinInfo (which only gets affinity but even for inactive domains)
|
|
|
|
vcpuPinInfo, _, err = l.libvirt.DomainGetVcpuPinInfo(domain, int32(vCPUs), int32(bytesToHoldPCPUs), uint32(golibvirt.DomainAffectCurrent))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
var vcpuAffinities []vcpuAffinity
|
|
for i := 0; i < int(vCPUs); i++ {
|
|
var coresAffinity []string
|
|
for j := 0; j < pCPUs; j++ {
|
|
aByte := (i * bytesToHoldPCPUs) + (j / 8)
|
|
aBit := j % 8
|
|
|
|
if (vcpuPinInfo[aByte] & (1 << uint(aBit))) != 0 {
|
|
coresAffinity = append(coresAffinity, strconv.Itoa(j))
|
|
}
|
|
}
|
|
|
|
vcpu := vcpuAffinity{
|
|
vcpuID: strconv.FormatInt(int64(i), 10),
|
|
coresAffinity: strings.Join(coresAffinity, ","),
|
|
currentPCPUID: -1,
|
|
}
|
|
|
|
if shouldGetCurrentPCPU && i < len(cpuInfo) {
|
|
vcpu.currentPCPUID = cpuInfo[i].CPU
|
|
}
|
|
|
|
if len(coresAffinity) > 0 {
|
|
vcpuAffinities = append(vcpuAffinities, vcpu)
|
|
}
|
|
}
|
|
|
|
return vcpuAffinities, nil
|
|
}
|
|
|
|
func (l *utilsImpl) ensureConnected(libvirtURI string) error {
|
|
if isConnected(l.libvirt) {
|
|
return nil
|
|
}
|
|
|
|
driver, err := libvirtutils.ConnectByUriString(libvirtURI)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.libvirt = driver
|
|
return nil
|
|
}
|
|
|
|
func (l *utilsImpl) disconnect() error {
|
|
l.libvirt = nil
|
|
return nil
|
|
}
|
|
|
|
func isConnected(driver *golibvirt.Libvirt) bool {
|
|
if driver == nil {
|
|
return false
|
|
}
|
|
|
|
select {
|
|
case <-driver.Disconnected():
|
|
return false
|
|
default:
|
|
}
|
|
return true
|
|
}
|