2024-03-12 05:35:32 +00:00
// 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.
2024-03-11 18:48:14 +00:00
package host
2024-03-11 02:32:11 +00:00
import (
2024-03-13 04:17:42 +00:00
"sync"
2024-03-11 02:32:11 +00:00
2024-03-11 18:48:14 +00:00
"git.staur.ca/stobbsm/clustvirt/lib/guest"
2024-03-24 04:05:06 +00:00
"git.staur.ca/stobbsm/clustvirt/lib/log"
2024-03-11 02:32:11 +00:00
"libvirt.org/go/libvirt"
2024-03-19 15:13:44 +00:00
"libvirt.org/go/libvirtxml"
2024-03-11 02:32:11 +00:00
)
// 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 {
2024-03-13 04:17:42 +00:00
// HostName used to make the connection
HostName string
// SystemHostName is the hostname as reported by the system itself
SystemHostName string
// LibVersion is the version of Libvirt on the host
LibVersion uint32
// SysInfo is the XML representation of the host system information
SysInfo string
// Alive indicates if the connection is alive
Alive bool
// Encrypted indicates if the connection is encrypted
Encrypted bool
// Secure indicates if the connection is secure
Secure bool
2024-03-24 04:05:06 +00:00
// HostInfo provides basic HW information about a host
HostInfo * libvirtxml . CapsHost
// NodeMemory provides basic memory information about the Host
NodeMemory * NodeMemoryInfo
2024-03-13 04:17:42 +00:00
// VMList is the list of virtual machines available to the host
2024-03-24 04:05:06 +00:00
VMList [ ] * libvirtxml . Domain
2024-03-13 04:17:42 +00:00
// NetIfList is the list of network interfaces on the host
2024-03-24 04:05:06 +00:00
NetIfFList [ ] * libvirtxml . Interface
2024-03-13 04:17:42 +00:00
// NetworkList is the list of defined networks on the host
2024-03-24 04:05:06 +00:00
NetworkList [ ] * libvirtxml . Network
2024-03-13 04:17:42 +00:00
// DeviceList is the list of devices on the host
2024-03-24 04:05:06 +00:00
DeviceList [ ] * libvirtxml . NodeDevice
2024-03-13 04:17:42 +00:00
// SecretList provides a list of secrets available to the host
2024-03-24 04:05:06 +00:00
SecretList [ ] * libvirtxml . Secret
2024-03-13 04:17:42 +00:00
// StoragePoolList provides the list of stoarge ppols available to the host
2024-03-24 04:05:06 +00:00
StoragePoolList [ ] * libvirtxml . StoragePool
// VolumeList is the list of volumes available on the host
VolumeList [ ] * libvirtxml . StorageVolume
2024-03-11 02:32:11 +00:00
2024-03-12 05:35:32 +00:00
uri * URI
2024-03-11 02:32:11 +00:00
conn * libvirt . Connect
close chan struct { }
closeErr chan error
}
2024-03-12 07:38:29 +00:00
// NodeMemoryInfo provides statistis about node memory usage from libvirt.NodeMemoryStats
type NodeMemoryInfo struct {
Total uint64
Free uint64
Buffers uint64
Cached uint64
}
2024-03-11 02:32:11 +00:00
// ConnectHost creates a host connection wrapper that can be used regularly
2024-03-12 05:35:32 +00:00
func ConnectHost ( uri * URI , host string ) ( * Host , error ) {
2024-03-11 02:32:11 +00:00
h := & Host {
HostName : host ,
2024-03-12 05:35:32 +00:00
uri : uri ,
2024-03-11 02:32:11 +00:00
}
if err := h . connect ( ) ; err != nil {
return nil , err
}
h . close = make ( chan struct { } )
h . closeErr = make ( chan error )
2024-03-13 04:17:42 +00:00
h . getInfo ( )
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 {
2024-03-24 04:05:06 +00:00
log . Info ( "Host.Close" ) . Str ( "hostname" , h . HostName ) . Msg ( "closing connection" )
2024-03-13 04:17:42 +00:00
close ( h . close )
return <- h . closeErr
}
// private methods that load the different informational parts
func ( h * Host ) getInfo ( ) {
2024-03-24 04:05:06 +00:00
wg := new ( sync . WaitGroup )
2024-03-13 04:17:42 +00:00
2024-03-24 04:05:06 +00:00
log . Info ( "host.getInfo" ) . Str ( "hostname" , h . HostName ) . Msg ( "collecting host information" )
2024-03-19 15:13:44 +00:00
infoFuncs := [ ] func ( ) {
2024-03-24 04:05:06 +00:00
h . hostInfo ,
h . memoryInfo ,
h . getStoragePools ,
2024-03-13 04:17:42 +00:00
h . getNetsInfo ,
2024-03-24 04:05:06 +00:00
h . getIfaceInfo ,
h . getDevicesInfo ,
h . getDomainInfo ,
h . getSecretsInfo ,
2024-03-13 04:17:42 +00:00
}
for _ , f := range infoFuncs {
wg . Add ( 1 )
2024-03-19 15:13:44 +00:00
go func ( wg * sync . WaitGroup ) {
defer wg . Done ( )
f ( )
} ( wg )
2024-03-13 04:17:42 +00:00
}
wg . Wait ( )
}
2024-03-24 04:05:06 +00:00
func ( h * Host ) hostInfo ( ) {
log . Info ( "host.hostInfo" ) . Str ( "hostname" , h . HostName ) . Msg ( "collecting hostinfo" )
2024-03-12 05:35:32 +00:00
var err error
2024-03-24 04:05:06 +00:00
rawxml , err := h . conn . GetCapabilities ( )
2024-03-12 05:35:32 +00:00
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.hostInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
}
xmldoc := & libvirtxml . Caps { }
if err = xmldoc . Unmarshal ( rawxml ) ; err != nil {
log . Error ( "Host.hostInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
2024-03-12 05:35:32 +00:00
}
2024-03-24 04:05:06 +00:00
h . HostInfo = & xmldoc . Host
2024-03-12 07:38:29 +00:00
h . SystemHostName , err = h . conn . GetHostname ( )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.hostInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Msg ( "unable to set SystemHostName" )
2024-03-12 07:38:29 +00:00
}
if h . SystemHostName == "" {
h . SystemHostName = h . HostName
}
2024-03-24 04:05:06 +00:00
log . Info ( "Host.hostInfo" ) . Str ( "hostname" , h . HostName ) . Str ( "system hostname" , h . SystemHostName ) . Msg ( "set system hostname" )
2024-03-12 07:38:29 +00:00
h . LibVersion , err = h . conn . GetLibVersion ( )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.hostInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Msg ( "unable to get libversion" )
return
2024-03-12 07:38:29 +00:00
}
2024-03-24 04:05:06 +00:00
log . Info ( "Host.hostInfo" ) . Str ( "hostname" , h . HostName ) . Uint32 ( "libversion" , h . LibVersion ) . Send ( )
}
2024-03-12 07:38:29 +00:00
2024-03-24 04:05:06 +00:00
func ( h * Host ) memoryInfo ( ) {
2024-03-12 07:38:29 +00:00
mi , err := h . conn . GetMemoryStats ( libvirt . NODE_MEMORY_STATS_ALL_CELLS , 0 )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.memoryInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Send ( )
2024-03-12 07:38:29 +00:00
}
2024-03-24 04:05:06 +00:00
h . NodeMemory = & NodeMemoryInfo {
Total : mi . Total ,
Free : mi . Free ,
Buffers : mi . Buffers ,
Cached : mi . Cached ,
2024-03-12 07:38:29 +00:00
}
h . SysInfo , err = h . conn . GetSysinfo ( 0 )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.memoryInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Msg ( "failed to GetSysInfo" )
2024-03-12 07:38:29 +00:00
}
h . Alive , err = h . conn . IsAlive ( )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.memoryInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Msg ( "failed check to IsAlive" )
2024-03-12 07:38:29 +00:00
}
h . Encrypted , err = h . conn . IsEncrypted ( )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.memoryInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Msg ( "failed to check IsEncrypted" )
2024-03-12 07:38:29 +00:00
}
h . Secure , err = h . conn . IsSecure ( )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.memoryInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Msg ( "failed to check IsSecure" )
2024-03-12 07:38:29 +00:00
}
2024-03-13 04:17:42 +00:00
}
2024-03-12 07:38:29 +00:00
2024-03-24 04:05:06 +00:00
func ( h * Host ) getStoragePools ( ) {
log . Info ( "host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Msg ( "collection storage pool information" )
// Get list of all storage pools on the host
spools , err := h . conn . ListAllStoragePools ( 0 )
2024-03-13 04:17:42 +00:00
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Msg ( "failed to ListAllStoragePools" )
}
if len ( spools ) > 0 {
h . StoragePoolList = make ( [ ] * libvirtxml . StoragePool , len ( spools ) )
for i , s := range spools {
// run in a function to allow defer s.Free()
func ( ) {
defer s . Free ( )
// Get the XML represenation of each storage pool, parse it with libvirtxml
rawxml , err := s . GetXMLDesc ( 0 )
if err != nil {
log . Error ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
return
}
xmldoc := & libvirtxml . StoragePool { }
err = xmldoc . Unmarshal ( rawxml )
if err != nil {
log . Error ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
return
}
h . StoragePoolList [ i ] = xmldoc
// Get list of all storage volumes in the current storage pool
svols , err := s . ListAllStorageVolumes ( 0 )
if err != nil {
log . Error ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Str ( "storagepool" , h . StoragePoolList [ i ] . Name ) . Err ( err ) . Msg ( "failed to ListAllStorageVolumes" )
}
if len ( svols ) > 0 {
// define temporary variable to hold slice of StorageVolume, that can
// either be appended to the existing slice, or used in place of the newly
// defined slice
tvl := make ( [ ] * libvirtxml . StorageVolume , len ( svols ) )
for j , sv := range svols {
// run in a function so I can defer the sv.Free() call
func ( ) {
defer sv . Free ( )
rawxml , err = sv . GetXMLDesc ( 0 )
if err != nil {
log . Error ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
return
}
xmldoc := & libvirtxml . StorageVolume { }
err = xmldoc . Unmarshal ( rawxml )
if err != nil {
log . Error ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
return
}
tvl [ j ] = xmldoc
log . Info ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Str ( "added volume" , tvl [ j ] . Name ) . Send ( )
} ( )
}
// Append the contents of tvl to h.VolumeList
if h . VolumeList == nil {
log . Info ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Str ( "storagepool" , h . StoragePoolList [ i ] . Name ) . Msg ( "initializing VolumeList" )
h . VolumeList = [ ] * libvirtxml . StorageVolume { }
}
// Only append if the temporary storage volume isn't nil
log . Info ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Str ( "storagepool" , h . StoragePoolList [ i ] . Name ) . Int ( "VolumeList count" , len ( h . VolumeList ) ) . Msg ( "before filter" )
for _ , tsv := range tvl {
if tsv != nil {
h . VolumeList = append ( h . VolumeList , tsv )
}
}
log . Info ( "Host.getStoragePools" ) . Str ( "hostname" , h . HostName ) . Str ( "storagepool" , h . StoragePoolList [ i ] . Name ) . Int ( "VolumeList count" , len ( h . VolumeList ) ) . Msg ( "after filter" )
}
} ( )
2024-03-13 04:17:42 +00:00
}
}
2024-03-24 04:05:06 +00:00
}
func ( h * Host ) getSecretsInfo ( ) {
log . Info ( "host.getSecretsInfo" ) . Str ( "hostname" , h . HostName ) . Msg ( "collecting secret information" )
nsecrets , err := h . conn . ListAllSecrets ( 0 )
if err != nil {
log . Error ( "Host.getSecretsInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Send ( )
}
if len ( nsecrets ) > 0 {
h . SecretList = make ( [ ] * libvirtxml . Secret , len ( nsecrets ) )
for i , s := range nsecrets {
func ( ) {
defer s . Free ( )
rawxml , err := s . GetXMLDesc ( 0 )
if err != nil {
log . Error ( "Host.getSecretsInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
}
xmldoc := & libvirtxml . Secret { }
if err = xmldoc . Unmarshal ( rawxml ) ; err != nil {
log . Error ( "Host.getSecretsInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
}
h . SecretList [ i ] = xmldoc
} ( )
}
2024-03-13 04:17:42 +00:00
}
2024-03-11 02:32:11 +00:00
}
2024-03-19 15:13:44 +00:00
func ( h * Host ) getDomainInfo ( ) {
2024-03-24 04:05:06 +00:00
log . Info ( "host.getDomainInfo" ) . Str ( "hostname" , h . HostName ) . Msg ( "collecting domain (vm) information" )
2024-03-13 04:17:42 +00:00
// getDomainInfo
doms , err := h . conn . ListAllDomains ( 0 )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.getDomainInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Send ( )
return
2024-03-13 04:17:42 +00:00
}
if len ( doms ) > 0 {
2024-03-24 04:05:06 +00:00
h . VMList = make ( [ ] * libvirtxml . Domain , len ( doms ) )
2024-03-13 04:17:42 +00:00
for i , d := range doms {
2024-03-24 04:05:06 +00:00
func ( ) {
defer d . Free ( )
rawxml , err := d . GetXMLDesc ( 0 )
if err != nil {
log . Error ( "Host.getDomainInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
return
}
h . VMList [ i ] = & libvirtxml . Domain { }
if err = h . VMList [ i ] . Unmarshal ( rawxml ) ; err != nil {
log . Error ( "Host.getDomainInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
return
}
} ( )
2024-03-13 04:17:42 +00:00
}
}
2024-03-11 02:32:11 +00:00
}
2024-03-19 15:13:44 +00:00
func ( h * Host ) getIfaceInfo ( ) {
2024-03-13 04:17:42 +00:00
// getIfaceInfo
2024-03-24 04:05:06 +00:00
ifaces , err := h . conn . ListAllInterfaces ( 0 )
2024-03-13 04:17:42 +00:00
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.getIfaceInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Send ( )
2024-03-11 02:32:11 +00:00
}
2024-03-13 04:17:42 +00:00
if len ( ifaces ) > 0 {
2024-03-24 04:05:06 +00:00
h . NetIfFList = make ( [ ] * libvirtxml . Interface , len ( ifaces ) )
2024-03-13 04:17:42 +00:00
for i , ni := range ifaces {
2024-03-24 04:05:06 +00:00
func ( ) {
defer ni . Free ( )
rawxml , err := ni . GetXMLDesc ( 0 )
if err != nil {
log . Error ( "Host.getIfaceInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
return
}
h . NetIfFList [ i ] = & libvirtxml . Interface { }
if err = h . NetIfFList [ i ] . Unmarshal ( rawxml ) ; err != nil {
log . Error ( "Host.getIfaceInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
return
}
} ( )
2024-03-13 04:17:42 +00:00
}
2024-03-11 18:48:14 +00:00
}
2024-03-11 02:32:11 +00:00
}
2024-03-19 15:13:44 +00:00
func ( h * Host ) getNetsInfo ( ) {
2024-03-13 04:17:42 +00:00
// getNetsInfo
2024-03-24 04:05:06 +00:00
nets , err := h . conn . ListAllNetworks ( 0 )
2024-03-13 04:17:42 +00:00
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.getNetsInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Send ( )
2024-03-13 04:17:42 +00:00
}
if len ( nets ) > 0 {
2024-03-24 04:05:06 +00:00
h . NetworkList = make ( [ ] * libvirtxml . Network , len ( nets ) )
for i , net := range nets {
func ( ) {
defer net . Free ( )
rawxml , err := net . GetXMLDesc ( 0 )
if err != nil {
log . Error ( "Host.getNetsInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
return
}
h . NetworkList [ i ] = & libvirtxml . Network { }
if err = h . NetworkList [ i ] . Unmarshal ( rawxml ) ; err != nil {
log . Error ( "Host.getNetsInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
return
}
} ( )
2024-03-13 04:17:42 +00:00
}
}
}
2024-03-19 15:13:44 +00:00
func ( h * Host ) getDevicesInfo ( ) {
2024-03-13 04:17:42 +00:00
ndevs , err := h . conn . ListAllNodeDevices ( 0 )
if err != nil {
2024-03-24 04:05:06 +00:00
log . Error ( "Host.getDevicesInfo" ) . Str ( "hostname" , h . HostName ) . Err ( err ) . Send ( )
2024-03-13 04:17:42 +00:00
}
if len ( ndevs ) > 0 {
2024-03-24 04:05:06 +00:00
h . DeviceList = make ( [ ] * libvirtxml . NodeDevice , len ( ndevs ) )
2024-03-13 04:17:42 +00:00
for i , dev := range ndevs {
2024-03-24 04:05:06 +00:00
func ( ) {
defer dev . Free ( )
rawxml , err := dev . GetXMLDesc ( 0 )
if err != nil {
log . Error ( "Host.getDevicesInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrGetXML ) . Err ( err ) . Send ( )
return
}
h . DeviceList [ i ] = & libvirtxml . NodeDevice { }
if err = h . DeviceList [ i ] . Unmarshal ( rawxml ) ; err != nil {
log . Error ( "Host.getDevicesInfo" ) . Str ( "hostname" , h . HostName ) . Err ( ErrParseXML ) . Err ( err ) . Send ( )
return
}
} ( )
2024-03-13 04:17:42 +00:00
}
}
2024-03-11 02:32:11 +00:00
}