clustvirt/lib/host/host.go
2024-03-11 23:35:32 -06:00

157 lines
4.0 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
SystemHomeName string
FreeMemory uint64
LibVersion uint32
HostInfo NodeInfo
HostSEVInfo SEVInfo
AvailableCPUTypes []string
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
}
// 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)
}
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
}