2024-03-11 18:48:14 +00:00
|
|
|
// Package guest implements utilities and structured data for libvirt virtual machines
|
|
|
|
package guest
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2024-03-12 01:39:17 +00:00
|
|
|
"log"
|
2024-03-11 18:48:14 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"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
|
|
|
|
}
|
2024-03-12 01:39:17 +00:00
|
|
|
if g.ID, err = g.dom.GetID(); err != nil {
|
2024-03-11 18:48:14 +00:00
|
|
|
return g, err
|
|
|
|
}
|
2024-03-12 01:39:17 +00:00
|
|
|
|
2024-03-11 18:48:14 +00:00
|
|
|
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 = setNotSet(blkiop.DeviceWeight, blkiop.DeviceWeightSet)
|
|
|
|
g.BlockIOParameters.DeviceReadIops = setNotSet(blkiop.DeviceReadIops, blkiop.DeviceReadIopsSet)
|
|
|
|
g.BlockIOParameters.DeviceWriteIops = setNotSet(blkiop.DeviceWriteIops, blkiop.DeviceWriteIopsSet)
|
|
|
|
g.BlockIOParameters.DeviceReadBps = setNotSet(blkiop.DeviceReadBps, blkiop.DeviceReadBpsSet)
|
|
|
|
g.BlockIOParameters.DeviceWriteBps = 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 = setNotSet(n.Name, n.NameSet)
|
|
|
|
g.Disks[i].Alias = setNotSet(n.Alias, n.AliasSet)
|
|
|
|
g.Disks[i].Partition = n.Partition
|
|
|
|
g.Disks[i].GuestAlias = 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] = 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 = setNotSet(n.Name, n.NameSet)
|
|
|
|
g.Users[i].Domain = setNotSet(n.Domain, n.DomainSet)
|
|
|
|
g.Users[i].LoginTime = time.Unix(int64(n.LoginTime), 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g.OS.ID = setNotSet(info.OS.ID, info.OS.IDSet)
|
|
|
|
g.OS.Name = setNotSet(info.OS.Name, info.OS.NameSet)
|
|
|
|
g.OS.Machine = setNotSet(info.OS.Machine, info.OS.MachineSet)
|
|
|
|
g.OS.Variant = setNotSet(info.OS.Variant, info.OS.VariantSet)
|
|
|
|
g.OS.Version = setNotSet(info.OS.Version, info.OS.VersionSet)
|
|
|
|
g.OS.VariantID = setNotSet(info.OS.VariantID, info.OS.VariantIDSet)
|
|
|
|
g.OS.VersionID = setNotSet(info.OS.VersionID, info.OS.VersionIDSet)
|
|
|
|
g.OS.PrettyName = setNotSet(info.OS.PrettyName, info.OS.PrettyNameSet)
|
|
|
|
g.OS.KernelRelease = setNotSet(info.OS.KernelRelease, info.OS.KernelReleaseSet)
|
|
|
|
g.OS.KernelVersion = setNotSet(info.OS.KernelVersion, info.OS.KernelVersionSet)
|
|
|
|
g.TimeZone.Name = 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 = setNotSet(n.Hwaddr, n.HwaddrSet)
|
|
|
|
g.NetIFace[i].Name = 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 = setNotSet(a.Addr, a.AddrSet)
|
|
|
|
g.NetIFace[i].IP[j].Type = setNotSet(a.Type, a.TypeSet)
|
|
|
|
g.NetIFace[i].IP[j].Prefix = a.Prefix
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-12 01:39:17 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2024-03-11 18:48:14 +00:00
|
|
|
go func() {
|
|
|
|
defer close(g.closeErr)
|
|
|
|
<-g.close
|
|
|
|
g.closeErr <- g.dom.Free()
|
|
|
|
}()
|
|
|
|
|
|
|
|
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
|
|
|
|
func (g *VM) Close() error {
|
2024-03-12 01:39:17 +00:00
|
|
|
log.Println("Closing VM", g.Name)
|
2024-03-11 18:48:14 +00:00
|
|
|
close(g.close)
|
|
|
|
return <-g.closeErr
|
|
|
|
}
|