clustvirt/lib/guest/domains.go

220 lines
6.4 KiB
Go
Raw Normal View History

// Package guest implements utilities and structured data for libvirt virtual machines
package guest
import (
"errors"
"log"
"time"
"git.staur.ca/stobbsm/clustvirt/util"
"libvirt.org/go/libvirt"
)
// Errors
// ErrVMNotFound error when virtual machine is not found
var ErrVMNotFound = errors.New("virtual machine not found")
// 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.Printf("WARNING: While loading backup information: %s", err)
}
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.Println("Closing VM", g.Name)
close(g.close)
return <-g.closeErr
}