Matthew Stobbs
1113062d9e
- lib/log is now git.staur.ca/stobbsm/simplelog - router is now git.staur.ca/stobbsm/simpleroute
269 lines
8.2 KiB
Go
269 lines
8.2 KiB
Go
// Package guest implements utilities and structured data for libvirt virtual machines
|
|
package guest
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"git.staur.ca/stobbsm/clustvirt/util"
|
|
log "git.staur.ca/stobbsm/simplelog"
|
|
"libvirt.org/go/libvirt"
|
|
)
|
|
|
|
// Errors
|
|
|
|
// ErrVMNotFound error when virtual machine is 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
|
|
type VM struct {
|
|
Name string
|
|
HostName string
|
|
ID uint
|
|
BackupXML string
|
|
Autostart bool
|
|
BlockIOParameters BlockIOParametersInfo
|
|
Disks []DiskInfo
|
|
Users []UserInfo
|
|
OS OSInfo
|
|
TimeZone TimeZoneInfo
|
|
NetIFace []NetIFaceInfo
|
|
|
|
close chan struct{}
|
|
closeErr chan error
|
|
dom *libvirt.Domain
|
|
}
|
|
|
|
// BlockIOParametersInfo is the struct describing VM block IO parameters
|
|
type BlockIOParametersInfo struct {
|
|
Weight uint
|
|
DeviceWeight string
|
|
DeviceReadIops string
|
|
DeviceWriteIops string
|
|
DeviceReadBps string
|
|
DeviceWriteBps string
|
|
}
|
|
|
|
// DiskInfo is the struct for guest disk information, and is used in VM
|
|
type DiskInfo struct {
|
|
Name string
|
|
Partition bool
|
|
Alias string
|
|
GuestAlias string
|
|
DiskDependency []string
|
|
}
|
|
|
|
// UserInfo is the struct for VM user information, and is used in VM
|
|
type UserInfo struct {
|
|
Name string
|
|
Domain string
|
|
LoginTime time.Time
|
|
}
|
|
|
|
// OSInfo is the struct for guest operating system information, and is used in VM
|
|
type OSInfo struct {
|
|
ID string
|
|
Name string
|
|
PrettyName string
|
|
Version string
|
|
VersionID string
|
|
KernelRelease string
|
|
KernelVersion string
|
|
Machine string
|
|
Variant string
|
|
VariantID string
|
|
}
|
|
|
|
// TimeZoneInfo provides guest timezone information, and is used in VM
|
|
type TimeZoneInfo struct {
|
|
Name string
|
|
Offset int
|
|
}
|
|
|
|
// NetIFaceInfo provides guest Network Interface information, and is used in VM
|
|
type NetIFaceInfo struct {
|
|
Name string
|
|
HWAddr string
|
|
IP []IPInfo
|
|
}
|
|
|
|
// IPInfo provides guest IP network information, and is used in NetIFaceInfo
|
|
type IPInfo struct {
|
|
Type string
|
|
Addr string
|
|
Prefix uint
|
|
}
|
|
|
|
// GetGuest loads guest information by name and returns it
|
|
func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
|
|
g := &VM{
|
|
Name: name,
|
|
close: make(chan struct{}),
|
|
closeErr: make(chan error),
|
|
}
|
|
|
|
var err error
|
|
|
|
// If the domain can't be found by name, exit early with an error
|
|
if g.dom, err = conn.LookupDomainByName(name); err != nil {
|
|
return g, err
|
|
}
|
|
if g.ID, err = g.dom.GetID(); err != nil {
|
|
return g, err
|
|
}
|
|
|
|
if g.Autostart, err = g.dom.GetAutostart(); err != nil {
|
|
return g, err
|
|
}
|
|
|
|
blkiop, err := g.dom.GetBlkioParameters(0)
|
|
if err != nil {
|
|
return g, err
|
|
}
|
|
g.BlockIOParameters.Weight = blkiop.Weight
|
|
g.BlockIOParameters.DeviceWeight = util.SetNotSet(blkiop.DeviceWeight, blkiop.DeviceWeightSet)
|
|
g.BlockIOParameters.DeviceReadIops = util.SetNotSet(blkiop.DeviceReadIops, blkiop.DeviceReadIopsSet)
|
|
g.BlockIOParameters.DeviceWriteIops = util.SetNotSet(blkiop.DeviceWriteIops, blkiop.DeviceWriteIopsSet)
|
|
g.BlockIOParameters.DeviceReadBps = util.SetNotSet(blkiop.DeviceReadBps, blkiop.DeviceReadBpsSet)
|
|
g.BlockIOParameters.DeviceWriteBps = util.SetNotSet(blkiop.DeviceWriteBps, blkiop.DeviceWriteBpsSet)
|
|
|
|
// Set as much guest info as possible
|
|
info, err := g.dom.GetGuestInfo(
|
|
libvirt.DOMAIN_GUEST_INFO_USERS|libvirt.DOMAIN_GUEST_INFO_OS|
|
|
libvirt.DOMAIN_GUEST_INFO_DISKS|libvirt.DOMAIN_GUEST_INFO_HOSTNAME|
|
|
libvirt.DOMAIN_GUEST_INFO_INTERFACES|libvirt.DOMAIN_GUEST_INFO_TIMEZONE|
|
|
libvirt.DOMAIN_GUEST_INFO_FILESYSTEM,
|
|
0,
|
|
)
|
|
if err != nil {
|
|
return g, err
|
|
}
|
|
// Set disk info
|
|
if len(info.Disks) > 0 {
|
|
g.Disks = make([]DiskInfo, len(info.Disks))
|
|
for i, n := range info.Disks {
|
|
g.Disks[i].Name = util.SetNotSet(n.Name, n.NameSet)
|
|
g.Disks[i].Alias = util.SetNotSet(n.Alias, n.AliasSet)
|
|
g.Disks[i].Partition = n.Partition
|
|
g.Disks[i].GuestAlias = util.SetNotSet(n.GuestAlias, n.GuestAliasSet)
|
|
g.Disks[i].DiskDependency = make([]string, len(n.Dependencies))
|
|
for j, k := range n.Dependencies {
|
|
g.Disks[i].DiskDependency[j] = util.SetNotSet(k.Name, k.NameSet)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(info.Users) > 0 {
|
|
g.Users = make([]UserInfo, len(info.Users))
|
|
for i, n := range info.Users {
|
|
g.Users[i].Name = util.SetNotSet(n.Name, n.NameSet)
|
|
g.Users[i].Domain = util.SetNotSet(n.Domain, n.DomainSet)
|
|
g.Users[i].LoginTime = time.Unix(int64(n.LoginTime), 0)
|
|
}
|
|
}
|
|
|
|
g.OS.ID = util.SetNotSet(info.OS.ID, info.OS.IDSet)
|
|
g.OS.Name = util.SetNotSet(info.OS.Name, info.OS.NameSet)
|
|
g.OS.Machine = util.SetNotSet(info.OS.Machine, info.OS.MachineSet)
|
|
g.OS.Variant = util.SetNotSet(info.OS.Variant, info.OS.VariantSet)
|
|
g.OS.Version = util.SetNotSet(info.OS.Version, info.OS.VersionSet)
|
|
g.OS.VariantID = util.SetNotSet(info.OS.VariantID, info.OS.VariantIDSet)
|
|
g.OS.VersionID = util.SetNotSet(info.OS.VersionID, info.OS.VersionIDSet)
|
|
g.OS.PrettyName = util.SetNotSet(info.OS.PrettyName, info.OS.PrettyNameSet)
|
|
g.OS.KernelRelease = util.SetNotSet(info.OS.KernelRelease, info.OS.KernelReleaseSet)
|
|
g.OS.KernelVersion = util.SetNotSet(info.OS.KernelVersion, info.OS.KernelVersionSet)
|
|
g.TimeZone.Name = util.SetNotSet(info.TimeZone.Name, info.TimeZone.NameSet)
|
|
g.TimeZone.Offset = info.TimeZone.Offset
|
|
|
|
// Set the hostname from the guest if it is set, otherwise use the VM name
|
|
if info.HostnameSet {
|
|
g.HostName = info.Hostname
|
|
} else {
|
|
g.HostName = g.Name
|
|
}
|
|
if len(info.Interfaces) > 0 {
|
|
g.NetIFace = make([]NetIFaceInfo, len(info.Interfaces))
|
|
for i, n := range info.Interfaces {
|
|
g.NetIFace[i].HWAddr = util.SetNotSet(n.Hwaddr, n.HwaddrSet)
|
|
g.NetIFace[i].Name = util.SetNotSet(n.Name, n.NameSet)
|
|
if len(n.Addrs) > 0 {
|
|
g.NetIFace[i].IP = make([]IPInfo, len(n.Addrs))
|
|
for j, a := range n.Addrs {
|
|
g.NetIFace[i].IP[j].Addr = util.SetNotSet(a.Addr, a.AddrSet)
|
|
g.NetIFace[i].IP[j].Type = util.SetNotSet(a.Type, a.TypeSet)
|
|
g.NetIFace[i].IP[j].Prefix = a.Prefix
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not errors, but still log the warnings when this happens
|
|
if g.BackupXML, err = g.dom.BackupGetXMLDesc(0); err != nil {
|
|
log.Warn("guest.GetGuest").Str("guest", g.Name).Err(err).Send()
|
|
}
|
|
|
|
go func() {
|
|
defer close(g.closeErr)
|
|
<-g.close
|
|
g.closeErr <- g.dom.Free()
|
|
}()
|
|
|
|
return g, nil
|
|
}
|
|
|
|
// Close closes an open connection
|
|
func (g *VM) Close() error {
|
|
log.Info("guest.Close").Str("guest", g.Name).Msg("closing vm")
|
|
close(g.close)
|
|
return <-g.closeErr
|
|
}
|