Compare commits

...

5 Commits

Author SHA1 Message Date
f96a911722 host will now load VM data
- All host node info is done using goroutines to speed up the process as much as possible
- The vast majority of data is stored at this point
  - Need to work out refreshes, using either polling or events
2024-03-12 22:17:42 -06:00
899a80f2e5 make file naming consistent 2024-03-12 10:34:16 -06:00
bb9feaef44 properly stubbed each library so it will compile 2024-03-12 10:33:14 -06:00
1bfdaf28c7 Add file stubs, add comments to guide dev in host
Signed-off-by: Matthew Stobbs <matthew@stobbs.ca>
2024-03-12 01:38:29 -06:00
b5ab70fc25 getting host data before each vm on host data
- Hostdata is useful
2024-03-11 23:35:32 -06:00
13 changed files with 696 additions and 109 deletions

4
go.mod
View File

@ -2,4 +2,6 @@ module git.staur.ca/stobbsm/clustvirt
go 1.22.1 go 1.22.1
require libvirt.org/go/libvirt v1.10001.0 // indirect require libvirt.org/go/libvirt v1.10001.0
require gopkg.in/ffmt.v1 v1.5.6 // indirect

2
go.sum
View File

@ -1,2 +1,4 @@
gopkg.in/ffmt.v1 v1.5.6 h1:4Bu3riZp5sAIXW2T/18JM9BkwJLodurXFR0f7PXp+cw=
gopkg.in/ffmt.v1 v1.5.6/go.mod h1:LssvGOZFiBGoBcobkTqnyh+uN1VzIRoibW+c0JI/Ha4=
libvirt.org/go/libvirt v1.10001.0 h1:lEVDNE7xfzmZXiDEGIS8NvJSuaz11OjRXw+ufbQEtPY= libvirt.org/go/libvirt v1.10001.0 h1:lEVDNE7xfzmZXiDEGIS8NvJSuaz11OjRXw+ufbQEtPY=
libvirt.org/go/libvirt v1.10001.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= libvirt.org/go/libvirt v1.10001.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ=

1
lib/device/lib.go Normal file
View File

@ -0,0 +1 @@
package device

View File

@ -6,6 +6,7 @@ import (
"log" "log"
"time" "time"
"git.staur.ca/stobbsm/clustvirt/util"
"libvirt.org/go/libvirt" "libvirt.org/go/libvirt"
) )
@ -14,6 +15,55 @@ import (
// ErrVMNotFound error when virtual machine is not found // ErrVMNotFound error when virtual machine is not found
var ErrVMNotFound = errors.New("virtual machine not found") var ErrVMNotFound = errors.New("virtual machine not found")
// VMState represents different states a virtual machine can be in
// Some states are mutually exclusive, but some can be applied at the
// same time.
// ie. A machine can be running, and migrating at the same time.
// A machine can be running, but had a failed migration
type VMState uint8
const (
// Steady States
// StateStopped means the VM isn't running
StateStopped VMState = iota
// StateRunning means the VM is currently running
StateRunning
// StateFailed means the VM is in a failed state, which can indicate a few things:
// The VM healthcheck (not yet defined, but will be) has failed
// A Migration has failed
// Adding or removing a device has failed
StateFailed
// StateMigrated means the VM was successfully migrated to a new host
StateMigrated
// StateHealthy means the VM had a successful healthcheck the last time it was done
StateHealthy
// StateUnhealthy means the opposite of healthy, as in the VM has failed a health check
StateUnhealthy
// Transient states
// StateStarted means the VM has been turned on, but is not nesicarily available yet
StateStarted
// StateRestarted means a reboot was triggered, but hasn't be finished yet
StateRestarted
// StatePoweroff means a shutdown was triggered, but isn't finished yet
StatePoweroff
// StateMigrate means a migration was started, but isn't done yet
StateMigrate
// StateCreate means a VM is being created
StateCreate
// StateInstall means a VM is being installed. This usually implies from an ISO and
// not an automated installation
StateInstall
// StateDestroy means a VM is scheduled or is in progress of being destroyed and deleted
StateDestroy
// StateBackup means a VM is in process of being backed up
StateBackup
// StateRestore means a VM is in process of being restored
StateRestore
)
// VM holds a handle is used to communicate to Domains // VM holds a handle is used to communicate to Domains
type VM struct { type VM struct {
Name string Name string
@ -120,11 +170,11 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
return g, err return g, err
} }
g.BlockIOParameters.Weight = blkiop.Weight g.BlockIOParameters.Weight = blkiop.Weight
g.BlockIOParameters.DeviceWeight = setNotSet(blkiop.DeviceWeight, blkiop.DeviceWeightSet) g.BlockIOParameters.DeviceWeight = util.SetNotSet(blkiop.DeviceWeight, blkiop.DeviceWeightSet)
g.BlockIOParameters.DeviceReadIops = setNotSet(blkiop.DeviceReadIops, blkiop.DeviceReadIopsSet) g.BlockIOParameters.DeviceReadIops = util.SetNotSet(blkiop.DeviceReadIops, blkiop.DeviceReadIopsSet)
g.BlockIOParameters.DeviceWriteIops = setNotSet(blkiop.DeviceWriteIops, blkiop.DeviceWriteIopsSet) g.BlockIOParameters.DeviceWriteIops = util.SetNotSet(blkiop.DeviceWriteIops, blkiop.DeviceWriteIopsSet)
g.BlockIOParameters.DeviceReadBps = setNotSet(blkiop.DeviceReadBps, blkiop.DeviceReadBpsSet) g.BlockIOParameters.DeviceReadBps = util.SetNotSet(blkiop.DeviceReadBps, blkiop.DeviceReadBpsSet)
g.BlockIOParameters.DeviceWriteBps = setNotSet(blkiop.DeviceWriteBps, blkiop.DeviceWriteBpsSet) g.BlockIOParameters.DeviceWriteBps = util.SetNotSet(blkiop.DeviceWriteBps, blkiop.DeviceWriteBpsSet)
// Set as much guest info as possible // Set as much guest info as possible
info, err := g.dom.GetGuestInfo( info, err := g.dom.GetGuestInfo(
@ -141,13 +191,13 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
if len(info.Disks) > 0 { if len(info.Disks) > 0 {
g.Disks = make([]DiskInfo, len(info.Disks)) g.Disks = make([]DiskInfo, len(info.Disks))
for i, n := range info.Disks { for i, n := range info.Disks {
g.Disks[i].Name = setNotSet(n.Name, n.NameSet) g.Disks[i].Name = util.SetNotSet(n.Name, n.NameSet)
g.Disks[i].Alias = setNotSet(n.Alias, n.AliasSet) g.Disks[i].Alias = util.SetNotSet(n.Alias, n.AliasSet)
g.Disks[i].Partition = n.Partition g.Disks[i].Partition = n.Partition
g.Disks[i].GuestAlias = setNotSet(n.GuestAlias, n.GuestAliasSet) g.Disks[i].GuestAlias = util.SetNotSet(n.GuestAlias, n.GuestAliasSet)
g.Disks[i].DiskDependency = make([]string, len(n.Dependencies)) g.Disks[i].DiskDependency = make([]string, len(n.Dependencies))
for j, k := range n.Dependencies { for j, k := range n.Dependencies {
g.Disks[i].DiskDependency[j] = setNotSet(k.Name, k.NameSet) g.Disks[i].DiskDependency[j] = util.SetNotSet(k.Name, k.NameSet)
} }
} }
} }
@ -155,23 +205,23 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
if len(info.Users) > 0 { if len(info.Users) > 0 {
g.Users = make([]UserInfo, len(info.Users)) g.Users = make([]UserInfo, len(info.Users))
for i, n := range info.Users { for i, n := range info.Users {
g.Users[i].Name = setNotSet(n.Name, n.NameSet) g.Users[i].Name = util.SetNotSet(n.Name, n.NameSet)
g.Users[i].Domain = setNotSet(n.Domain, n.DomainSet) g.Users[i].Domain = util.SetNotSet(n.Domain, n.DomainSet)
g.Users[i].LoginTime = time.Unix(int64(n.LoginTime), 0) g.Users[i].LoginTime = time.Unix(int64(n.LoginTime), 0)
} }
} }
g.OS.ID = setNotSet(info.OS.ID, info.OS.IDSet) g.OS.ID = util.SetNotSet(info.OS.ID, info.OS.IDSet)
g.OS.Name = setNotSet(info.OS.Name, info.OS.NameSet) g.OS.Name = util.SetNotSet(info.OS.Name, info.OS.NameSet)
g.OS.Machine = setNotSet(info.OS.Machine, info.OS.MachineSet) g.OS.Machine = util.SetNotSet(info.OS.Machine, info.OS.MachineSet)
g.OS.Variant = setNotSet(info.OS.Variant, info.OS.VariantSet) g.OS.Variant = util.SetNotSet(info.OS.Variant, info.OS.VariantSet)
g.OS.Version = setNotSet(info.OS.Version, info.OS.VersionSet) g.OS.Version = util.SetNotSet(info.OS.Version, info.OS.VersionSet)
g.OS.VariantID = setNotSet(info.OS.VariantID, info.OS.VariantIDSet) g.OS.VariantID = util.SetNotSet(info.OS.VariantID, info.OS.VariantIDSet)
g.OS.VersionID = setNotSet(info.OS.VersionID, info.OS.VersionIDSet) g.OS.VersionID = util.SetNotSet(info.OS.VersionID, info.OS.VersionIDSet)
g.OS.PrettyName = setNotSet(info.OS.PrettyName, info.OS.PrettyNameSet) g.OS.PrettyName = util.SetNotSet(info.OS.PrettyName, info.OS.PrettyNameSet)
g.OS.KernelRelease = setNotSet(info.OS.KernelRelease, info.OS.KernelReleaseSet) g.OS.KernelRelease = util.SetNotSet(info.OS.KernelRelease, info.OS.KernelReleaseSet)
g.OS.KernelVersion = setNotSet(info.OS.KernelVersion, info.OS.KernelVersionSet) g.OS.KernelVersion = util.SetNotSet(info.OS.KernelVersion, info.OS.KernelVersionSet)
g.TimeZone.Name = setNotSet(info.TimeZone.Name, info.TimeZone.NameSet) g.TimeZone.Name = util.SetNotSet(info.TimeZone.Name, info.TimeZone.NameSet)
g.TimeZone.Offset = info.TimeZone.Offset g.TimeZone.Offset = info.TimeZone.Offset
// Set the hostname from the guest if it is set, otherwise use the VM name // Set the hostname from the guest if it is set, otherwise use the VM name
@ -183,13 +233,13 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
if len(info.Interfaces) > 0 { if len(info.Interfaces) > 0 {
g.NetIFace = make([]NetIFaceInfo, len(info.Interfaces)) g.NetIFace = make([]NetIFaceInfo, len(info.Interfaces))
for i, n := range info.Interfaces { for i, n := range info.Interfaces {
g.NetIFace[i].HWAddr = setNotSet(n.Hwaddr, n.HwaddrSet) g.NetIFace[i].HWAddr = util.SetNotSet(n.Hwaddr, n.HwaddrSet)
g.NetIFace[i].Name = setNotSet(n.Name, n.NameSet) g.NetIFace[i].Name = util.SetNotSet(n.Name, n.NameSet)
if len(n.Addrs) > 0 { if len(n.Addrs) > 0 {
g.NetIFace[i].IP = make([]IPInfo, len(n.Addrs)) g.NetIFace[i].IP = make([]IPInfo, len(n.Addrs))
for j, a := range n.Addrs { for j, a := range n.Addrs {
g.NetIFace[i].IP[j].Addr = setNotSet(a.Addr, a.AddrSet) g.NetIFace[i].IP[j].Addr = util.SetNotSet(a.Addr, a.AddrSet)
g.NetIFace[i].IP[j].Type = setNotSet(a.Type, a.TypeSet) g.NetIFace[i].IP[j].Type = util.SetNotSet(a.Type, a.TypeSet)
g.NetIFace[i].IP[j].Prefix = a.Prefix g.NetIFace[i].IP[j].Prefix = a.Prefix
} }
} }
@ -210,14 +260,6 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
return g, nil return g, nil
} }
// helper function to set a string value to "NotSet" if it wasn't actually set
func setNotSet(v string, s bool) string {
if s {
return v
}
return "Notset"
}
// Close closes an open connection // Close closes an open connection
func (g *VM) Close() error { func (g *VM) Close() error {
log.Println("Closing VM", g.Name) log.Println("Closing VM", g.Name)

View File

@ -1,72 +0,0 @@
package host
import (
"fmt"
"log"
"git.staur.ca/stobbsm/clustvirt/lib/guest"
"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
conn *libvirt.Connect
close chan struct{}
closeErr chan error
}
// ConnectHost creates a host connection wrapper that can be used regularly
func ConnectHost(host string) (*Host, error) {
h := &Host{
HostName: host,
}
if err := h.connect(); err != nil {
return nil, err
}
h.close = make(chan struct{})
h.closeErr = make(chan error)
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(
fmt.Sprintf("qemu+ssh://%s/system", 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
}

502
lib/host/lib.go Normal file
View File

@ -0,0 +1,502 @@
// 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"
"sync"
"git.staur.ca/stobbsm/clustvirt/lib/guest"
"git.staur.ca/stobbsm/clustvirt/lib/secret"
"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 used to make the connection
HostName string
// SystemHostName is the hostname as reported by the system itself
SystemHostName string
// FreeMemory is the available free memory
FreeMemory uint64
// LibVersion is the version of Libvirt on the host
LibVersion uint32
// HostInfo provides basic HW information about a host
HostInfo NodeInfo
// HostSEVInfo provides informatoin about AMD SEV extentions available on the host
HostSEVInfo SEVInfo
// AvailableCPUTypes are the available types of CPUs that can be used for VM creation
AvailableCPUTypes []string
// NodeMemory provides basic memory information about the Host
NodeMemory NodeMemoryInfo
// StorageCapabilities is the XML representation of the hosts storage capabilities
StorageCapabilities string
// 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
// VMList is the list of virtual machines available to the host
VMList []VMInfo
// NetIfList is the list of network interfaces on the host
NetIfFList []NetIfInfo
// NetworkList is the list of defined networks on the host
NetworkList []NetworkInfo
// DeviceList is the list of devices on the host
DeviceList []DeviceInfo
// SecretList provides a list of secrets available to the host
SecretList []SecretInfo
// StoragePoolList provides the list of stoarge ppols available to the host
StoragePoolList []StoragePoolInfo
uri *URI
conn *libvirt.Connect
close chan struct{}
closeErr chan error
}
// DeviceInfo holds basic information for host devices
type DeviceInfo struct {
Name string
Capabilities []string
XML string
}
// VMInfo holds basic VM information, like the name and ID
type VMInfo struct {
Name string
ID uint
UUID []byte
XML string
// States are the current states active on the host
States []guest.VMState
}
// SecretInfo doesn't let you see the contents of secrets, but does let you see what secrets have
// been defined in a simple format.
type SecretInfo struct {
UUID string
XML string
Type string
}
// StoragePoolInfo holds basic information on storage pools
type StoragePoolInfo struct {
Name string
UUID []byte
Type string
XML string
Active bool
Persistent bool
// HAEnabled indicates if the storage pool has High Availability
HAEnabled bool
// Volumes defined in the storage pool
Volumes []VolumeInfo
}
// VolumeInfo holds basic information about Volumes available in storage pools
type VolumeInfo struct {
Name string
// StoragePool this volume is part of
StoragePool StoragePoolInfo
Type string
Size uint
XML string
}
// NetIfInfo holds basic information about available network interfaces (not their connections, the devices themselves)
type NetIfInfo struct {
Name string
MacAddr string
XML string
}
// NetworkInfo holds basic information about network connections
type NetworkInfo struct {
Name string
UUID []byte
XML string
// NetIf is the network interface this connection is applied to
NetIf NetIfInfo
}
// 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)
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 {
log.Println("Closing Host", h.HostName)
close(h.close)
return <-h.closeErr
}
// private methods that load the different informational parts
func (h *Host) getInfo() {
var wg sync.WaitGroup
infoFuncs := []func(*sync.WaitGroup){
h.getDevicesInfo,
h.getDomainInfo,
h.getIfaceInfo,
h.getNetsInfo,
h.getNodeInfo,
h.getSEVInfo,
h.getSecretsInfo,
h.getStoragePools,
}
for _, f := range infoFuncs {
wg.Add(1)
f(&wg)
}
wg.Wait()
}
func (h *Host) getStoragePools(wg *sync.WaitGroup) {
defer wg.Done()
spools, err := h.conn.ListAllStoragePools(0)
if err != nil {
log.Println(err)
}
if len(spools) > 0 {
h.StoragePoolList = make([]StoragePoolInfo, len(spools))
for i, s := range spools {
if h.StoragePoolList[i].XML, err = s.GetXMLDesc(0); err != nil {
log.Println(err)
}
if h.StoragePoolList[i].Name, err = s.GetName(); err != nil {
log.Println(err)
}
if h.StoragePoolList[i].UUID, err = s.GetUUID(); err != nil {
log.Println(err)
}
if h.StoragePoolList[i].Active, err = s.IsActive(); err != nil {
log.Println(err)
}
if h.StoragePoolList[i].Persistent, err = s.IsPersistent(); err != nil {
log.Println(err)
}
s.Free()
}
}
}
func (h *Host) getSecretsInfo(wg *sync.WaitGroup) {
defer wg.Done()
nsecrets, err := h.conn.ListAllSecrets(0)
if err != nil {
log.Println(err)
}
if len(nsecrets) > 0 {
h.SecretList = make([]SecretInfo, len(nsecrets))
for i, s := range nsecrets {
if h.SecretList[i].XML, err = s.GetXMLDesc(0); err != nil {
log.Println(err)
}
stype, err := s.GetUsageType()
if err != nil {
log.Println(err)
}
h.SecretList[i].Type = secret.SecretUsageTypeMap[stype]
s.Free()
}
}
}
func (h *Host) getNodeInfo(wg *sync.WaitGroup) {
defer wg.Done()
var err error
h.AvailableCPUTypes, err = h.conn.GetCPUModelNames("x86_64", 0)
if err != nil {
log.Println("Error getting cpu model names", err)
}
ni, err := h.conn.GetNodeInfo()
if err != nil {
log.Println(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
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)
}
}
func (h *Host) getSEVInfo(wg *sync.WaitGroup) {
defer wg.Done()
// getSEVInfo
h.HostSEVInfo.SEVEnabled = true
ns, err := h.conn.GetSEVInfo(0)
if err != nil {
log.Println(err)
lverr, ok := err.(libvirt.Error)
if ok {
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)
}
}
func (h *Host) getDomainInfo(wg *sync.WaitGroup) {
defer wg.Done()
// getDomainInfo
doms, err := h.conn.ListAllDomains(0)
if err != nil {
log.Println(err)
}
if len(doms) > 0 {
h.VMList = make([]VMInfo, len(doms))
for i, d := range doms {
// Just going to log errors here, and free the dom after getting what we can
if h.VMList[i].Name, err = d.GetName(); err != nil {
log.Println(err)
}
if h.VMList[i].UUID, err = d.GetUUID(); err != nil {
log.Println(err)
}
if h.VMList[i].ID, err = d.GetID(); err != nil {
log.Println(err)
}
if h.VMList[i].XML, err = d.GetXMLDesc(0); err != nil {
log.Println(err)
}
d.Free()
}
}
}
func (h *Host) getIfaceInfo(wg *sync.WaitGroup) {
defer wg.Done()
// getIfaceInfo
ifaces, err := h.conn.ListInterfaces()
if err != nil {
log.Println(err)
}
if len(ifaces) > 0 {
h.NetIfFList = make([]NetIfInfo, len(ifaces))
for i, ni := range ifaces {
h.NetIfFList[i].Name = ni
iface, err := h.conn.LookupInterfaceByName(ni)
if err != nil {
log.Println(err)
}
if h.NetIfFList[i].MacAddr, err = iface.GetMACString(); err != nil {
log.Println(err)
}
if h.NetIfFList[i].XML, err = iface.GetXMLDesc(0); err != nil {
log.Println(err)
}
iface.Free()
}
}
}
func (h *Host) getNetsInfo(wg *sync.WaitGroup) {
defer wg.Done()
// getNetsInfo
nets, err := h.conn.ListNetworks()
if err != nil {
log.Println(err)
}
if len(nets) > 0 {
h.NetworkList = make([]NetworkInfo, len(nets))
for i, netName := range nets {
net, err := h.conn.LookupNetworkByName(netName)
if err != nil {
log.Println(err)
}
if h.NetworkList[i].Name, err = net.GetName(); err != nil {
log.Println(err)
}
if h.NetworkList[i].UUID, err = net.GetUUID(); err != nil {
log.Println(err)
}
if h.NetworkList[i].XML, err = net.GetXMLDesc(0); err != nil {
log.Println(err)
}
net.Free()
}
}
}
func (h *Host) getDevicesInfo(wg *sync.WaitGroup) {
defer wg.Done()
ndevs, err := h.conn.ListAllNodeDevices(0)
if err != nil {
log.Println(err)
}
if len(ndevs) > 0 {
h.DeviceList = make([]DeviceInfo, len(ndevs))
for i, dev := range ndevs {
if h.DeviceList[i].Name, err = dev.GetName(); err != nil {
log.Println(err)
}
if h.DeviceList[i].Capabilities, err = dev.ListCaps(); err != nil {
log.Println(err)
}
if h.DeviceList[i].XML, err = dev.GetXMLDesc(0); err != nil {
log.Println(err)
}
dev.Free()
}
}
}

82
lib/host/uri.go Normal file
View File

@ -0,0 +1,82 @@
package host
import (
"log"
"strings"
)
// URI is a string type, accessed via the pre-defined variables, and represent
// the URI pattern used to connect to a host.
// Example:
// Driver[+Transport]://<host or empty for local>[:PORT]/<path>[?Options&in=uri&format]
type URI struct {
Driver string
Transport string
Path string
Options []string
}
// CustomURI create and return a custom URI method, following RFC2396,
// keeping in mind that the hostname will be inserted between the transport and path
func CustomURI(driver, transport, path string, options ...string) *URI {
return &URI{
Driver: driver,
Transport: transport,
Path: path,
Options: options,
}
}
// URIs available to build connections to a libvirt host
var (
// URI for connecting to a remote QEMU system over SSH
URI_QEMU_SSH_SYSTEM = &URI{Driver: "qemu", Transport: "ssh", Path: "system"}
// URI for connecting to a remote QEMU system over TLS
// Builds the URI qemu://<host:port>/system
URI_QEMU_TLS_SYSTEM = &URI{Driver: "qemu", Transport: "", Path: "system"}
// URI for connecting to a remote QEMU session over SSH
URI_QEMU_SSH_SESSION = &URI{Driver: "qemu", Transport: "ssh", Path: "session"}
// URI for connecting to a local QEMU system over a UNIX socket
URI_QEMU_UNIX_SYSTEM = &URI{Driver: "qemu", Transport: "unix", Path: "system"}
// URI for connecting to a remote QEMU system over unsecured TCP
URI_QEMU_TCP_SYSTEM = &URI{Driver: "qemu", Transport: "tcp", Path: "system"}
// URI for connecting to a remote XEN system with SSH
URI_XEN_SSH_SYSTEM = &URI{Driver: "xen", Transport: "ssh", Path: "system"}
// URI for connecting to a remote XEN system over TLS
URI_XEN_TLS_SYSTEM = &URI{Driver: "xen", Transport: "", Path: "system"}
)
// ConnectionString takes a host name to interpolate into a URI and returns the string
func (u *URI) ConnectionString(h string) string {
var sb strings.Builder
optlen := len(u.Options)
sb.WriteString(u.Driver)
if u.Transport != "" {
sb.WriteRune('+')
sb.WriteString(u.Transport)
}
sb.WriteString("://")
if h != "" {
sb.WriteString(h)
}
sb.WriteRune('/')
sb.WriteString(u.Path)
if optlen > 0 {
sb.WriteRune('?')
for i, o := range u.Options {
sb.WriteString(o)
if optlen != i+1 {
sb.WriteRune('&')
}
}
}
log.Printf("Connection URI: %s", sb.String())
return sb.String()
}
// AddOpt adds more options to the option list
func (u *URI) AddOpt(opt string) {
u.Options = append(u.Options, opt)
}

1
lib/netif/lib.go Normal file
View File

@ -0,0 +1 @@
package netif

1
lib/network/lib.go Normal file
View File

@ -0,0 +1 @@
package network

14
lib/secret/lib.go Normal file
View File

@ -0,0 +1,14 @@
package secret
import "libvirt.org/go/libvirt"
// SecretUsageTypeMap provides string representation to secret types used by
// libvirt
var SecretUsageTypeMap map[libvirt.SecretUsageType]string = map[libvirt.SecretUsageType]string{
libvirt.SECRET_USAGE_TYPE_NONE: "none",
libvirt.SECRET_USAGE_TYPE_VOLUME: "volume",
libvirt.SECRET_USAGE_TYPE_ISCSI: "iscsi",
libvirt.SECRET_USAGE_TYPE_VTPM: "vtpm",
libvirt.SECRET_USAGE_TYPE_TLS: "tls",
libvirt.SECRET_USAGE_TYPE_CEPH: "ceph",
}

1
lib/storagepool/lib.go Normal file
View File

@ -0,0 +1 @@
package storagepool

View File

@ -4,20 +4,22 @@ import (
"log" "log"
"git.staur.ca/stobbsm/clustvirt/lib/host" "git.staur.ca/stobbsm/clustvirt/lib/host"
ffmt "gopkg.in/ffmt.v1"
) )
func main() { func main() {
log.Println("Starting clustvirt, the libvirt cluster manager") log.Println("Starting clustvirt, the libvirt cluster manager")
venus, err := host.ConnectHost("venus.staur.ca") venus, err := host.ConnectHost(host.URI_QEMU_SSH_SYSTEM, "venus.staur.ca")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer venus.Close() defer venus.Close()
ffmt.P(venus)
lm, err := venus.GetGuestByName("logan-minecraft") lm, err := venus.GetGuestByName("logan-minecraft")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer lm.Close() defer lm.Close()
log.Println(lm) //ffmt.P(lm)
} }

9
util/util.go Normal file
View File

@ -0,0 +1,9 @@
package util
// helper function to set a string value to "NotSet" if it wasn't actually set
func SetNotSet(v string, s bool) string {
if s {
return v
}
return "Notset"
}