1
0
Fork 0
telegraf/plugins/inputs/libvirt/libvirt_utils.go

147 lines
4.3 KiB
Go
Raw Normal View History

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
}