clustvirt/lib/host/lib.go

240 lines
5.8 KiB
Go

// Package host provides utilities and data structures in relation to a libvirt host.
// This includes getting a list of virtual machines running on a host, launching
// a new virtual machine on a host, triggering a virtual machine migration to another
// host, getting hardware and resource usage from a host, and eventually more.
// Most of this is data at the moment, ensuring data can be gathered efficiently without
// slowing down the host from it's main job of running virtual machines.
package host
import (
"log"
"git.staur.ca/stobbsm/clustvirt/lib/guest"
"git.staur.ca/stobbsm/clustvirt/util"
"libvirt.org/go/libvirt"
)
// Host holds information and acts as a connection handle for a Host
// If a connection is closed prematurely, will re-open the connection and
// try the attempted method again
type Host struct {
HostName string
SystemHostName string
FreeMemory uint64
LibVersion uint32
HostInfo NodeInfo
HostSEVInfo SEVInfo
AvailableCPUTypes []string
NodeMemory NodeMemoryInfo
StorageCapabilities string
SysInfo string
Alive bool
Encrypted bool
Secure bool
// alldomains
VMList []string
// allinterfaces
IFList []string
// allnetworks
NetList []string
// alldevices
DeviceList []string
// allsecrets
SecretList []string
// allstoragepools
StoragePoolList []string
// defineddomains
// definedinterfaces
// definednetworks
// definedstoragepools
// listdomains []int id
// listinterfaces []string interfacename
// listnetworks []string networkname
// listserets []string secretname
// liststoragepools []string storagepoolname
uri *URI
conn *libvirt.Connect
close chan struct{}
closeErr chan error
}
// NodeInfo represents the basic HW info for a host node
type NodeInfo struct {
// livirt.NodeInfo section
Model string
Memory uint64
Cpus uint
MHz uint
Nodes uint32
Sockets uint32
Cores uint32
Threads uint32
}
// SEVInfo provides information about AMD SEV support
type SEVInfo struct {
// livirt.NodeSEVParameters section
SEVEnabled bool
PDH string
CertChain string
CBitPos uint
ReducedPhysBits uint
MaxGuests uint
MaxEsGuests uint
CPU0ID string
}
// NodeMemoryInfo provides statistis about node memory usage from libvirt.NodeMemoryStats
type NodeMemoryInfo struct {
Total uint64
Free uint64
Buffers uint64
Cached uint64
}
// ConnectHost creates a host connection wrapper that can be used regularly
func ConnectHost(uri *URI, host string) (*Host, error) {
h := &Host{
HostName: host,
uri: uri,
}
if err := h.connect(); err != nil {
return nil, err
}
h.close = make(chan struct{})
h.closeErr = make(chan error)
var err error
h.AvailableCPUTypes, err = h.conn.GetCPUModelNames("x86_64", 0)
if err != nil {
log.Println("Error getting cpu model names", err)
}
// Extract libvirt.NodeInfo and libvirt.NodeSEVParameters
ni, err := h.conn.GetNodeInfo()
if err != nil {
return nil, err
}
h.HostInfo.Model = ni.Model
h.HostInfo.Memory = ni.Memory
h.HostInfo.Cpus = ni.Cpus
h.HostInfo.MHz = ni.MHz
h.HostInfo.Nodes = ni.Nodes
h.HostInfo.Sockets = ni.Sockets
h.HostInfo.Cores = ni.Cores
h.HostInfo.Threads = ni.Threads
// Assume SEV is enabled, until we know otherwise
h.HostSEVInfo.SEVEnabled = true
ns, err := h.conn.GetSEVInfo(0)
if err != nil {
lverr, ok := err.(libvirt.Error)
if !ok {
return nil, err
}
switch lverr.Code {
case 84:
log.Println("SEV functions not supported")
h.HostSEVInfo.SEVEnabled = false
default:
log.Println("Error encountered", lverr.Error())
}
}
if h.HostSEVInfo.SEVEnabled {
h.HostSEVInfo.PDH = util.SetNotSet(ns.PDH, ns.PDHSet)
h.HostSEVInfo.CertChain = util.SetNotSet(ns.CertChain, ns.CertChainSet)
h.HostSEVInfo.CBitPos = ns.CBitPos
h.HostSEVInfo.ReducedPhysBits = ns.ReducedPhysBits
h.HostSEVInfo.MaxGuests = ns.MaxGuests
h.HostSEVInfo.MaxEsGuests = ns.MaxEsGuests
h.HostSEVInfo.CPU0ID = util.SetNotSet(ns.CPU0ID, ns.CPU0IDSet)
}
h.SystemHostName, err = h.conn.GetHostname()
if err != nil {
log.Println(err)
}
if h.SystemHostName == "" {
h.SystemHostName = h.HostName
}
h.LibVersion, err = h.conn.GetLibVersion()
if err != nil {
log.Println(err)
}
h.FreeMemory, err = h.conn.GetFreeMemory()
if err != nil {
log.Println(err)
}
mi, err := h.conn.GetMemoryStats(libvirt.NODE_MEMORY_STATS_ALL_CELLS, 0)
if err != nil {
log.Println(err)
}
h.NodeMemory.Total = mi.Total
h.NodeMemory.Free = mi.Free
h.NodeMemory.Buffers = mi.Buffers
h.NodeMemory.Cached = mi.Cached
h.StorageCapabilities, err = h.conn.GetStoragePoolCapabilities(0)
if err != nil {
log.Println(err)
}
h.SysInfo, err = h.conn.GetSysinfo(0)
if err != nil {
log.Println(err)
}
h.Alive, err = h.conn.IsAlive()
if err != nil {
log.Println(err)
}
h.Encrypted, err = h.conn.IsEncrypted()
if err != nil {
log.Println(err)
}
h.Secure, err = h.conn.IsSecure()
if err != nil {
log.Println(err)
}
go func() {
defer close(h.closeErr)
<-h.close
_, err := h.conn.Close()
h.closeErr <- err
}()
return h, nil
}
// connect creates a host connection
func (h *Host) connect() error {
var err error
h.conn, err = libvirt.NewConnect(h.uri.ConnectionString(h.HostName))
return err
}
// GetGuestByName returns a GuestVM instance that exists on the given host
func (h *Host) GetGuestByName(name string) (*guest.VM, error) {
g, err := guest.GetGuest(name, h.conn)
if err == nil {
return g, nil
}
lverr, ok := err.(libvirt.Error)
if ok && lverr.Code == libvirt.ERR_INVALID_CONN {
// try again after creating a new connection
return guest.GetGuest(name, h.conn)
}
return nil, err
}
// Close triggers closing the host connection
func (h *Host) Close() error {
log.Println("Closing Host", h.HostName)
close(h.close)
return <-h.closeErr
}