getting host data before each vm on host data
- Hostdata is useful
This commit is contained in:
parent
00758af30b
commit
b5ab70fc25
2
go.mod
2
go.mod
@ -2,4 +2,4 @@ module git.staur.ca/stobbsm/clustvirt
|
||||
|
||||
go 1.22.1
|
||||
|
||||
require libvirt.org/go/libvirt v1.10001.0 // indirect
|
||||
require libvirt.org/go/libvirt v1.10001.0
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.staur.ca/stobbsm/clustvirt/util"
|
||||
"libvirt.org/go/libvirt"
|
||||
)
|
||||
|
||||
@ -120,11 +121,11 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
|
||||
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)
|
||||
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(
|
||||
@ -141,13 +142,13 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
|
||||
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].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 = setNotSet(n.GuestAlias, n.GuestAliasSet)
|
||||
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] = setNotSet(k.Name, k.NameSet)
|
||||
g.Disks[i].DiskDependency[j] = util.SetNotSet(k.Name, k.NameSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,23 +156,23 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
|
||||
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].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 = 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.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
|
||||
@ -183,13 +184,13 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
|
||||
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)
|
||||
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 = setNotSet(a.Addr, a.AddrSet)
|
||||
g.NetIFace[i].IP[j].Type = setNotSet(a.Type, a.TypeSet)
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -210,14 +211,6 @@ func GetGuest(name string, conn *libvirt.Connect) (*VM, error) {
|
||||
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 {
|
||||
log.Println("Closing VM", g.Name)
|
||||
|
@ -1,10 +1,16 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.staur.ca/stobbsm/clustvirt/lib/guest"
|
||||
"git.staur.ca/stobbsm/clustvirt/util"
|
||||
"libvirt.org/go/libvirt"
|
||||
)
|
||||
|
||||
@ -12,17 +18,51 @@ import (
|
||||
// If a connection is closed prematurely, will re-open the connection and
|
||||
// try the attempted method again
|
||||
type Host struct {
|
||||
HostName string
|
||||
HostName string
|
||||
SystemHomeName string
|
||||
FreeMemory uint64
|
||||
LibVersion uint32
|
||||
HostInfo NodeInfo
|
||||
HostSEVInfo SEVInfo
|
||||
AvailableCPUTypes []string
|
||||
|
||||
uri *URI
|
||||
conn *libvirt.Connect
|
||||
close chan struct{}
|
||||
closeErr chan error
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// ConnectHost creates a host connection wrapper that can be used regularly
|
||||
func ConnectHost(host string) (*Host, error) {
|
||||
func ConnectHost(uri *URI, host string) (*Host, error) {
|
||||
h := &Host{
|
||||
HostName: host,
|
||||
uri: uri,
|
||||
}
|
||||
|
||||
if err := h.connect(); err != nil {
|
||||
@ -32,6 +72,52 @@ func ConnectHost(host string) (*Host, error) {
|
||||
h.close = make(chan struct{})
|
||||
h.closeErr = make(chan error)
|
||||
|
||||
var err error
|
||||
h.AvailableCPUTypes, err = h.conn.GetCPUModelNames("x86_64", 0)
|
||||
if err != nil {
|
||||
log.Println("Error getting cpu model names", err)
|
||||
}
|
||||
|
||||
// Extract libvirt.NodeInfo and libvirt.NodeSEVParameters
|
||||
ni, err := h.conn.GetNodeInfo()
|
||||
if err != nil {
|
||||
return nil, 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
|
||||
|
||||
// Assume SEV is enabled, until we know otherwise
|
||||
h.HostSEVInfo.SEVEnabled = true
|
||||
ns, err := h.conn.GetSEVInfo(0)
|
||||
if err != nil {
|
||||
lverr, ok := err.(libvirt.Error)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer close(h.closeErr)
|
||||
<-h.close
|
||||
@ -44,9 +130,7 @@ func ConnectHost(host string) (*Host, error) {
|
||||
// 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),
|
||||
)
|
||||
h.conn, err = libvirt.NewConnect(h.uri.ConnectionString(h.HostName))
|
||||
return err
|
||||
}
|
||||
|
||||
|
82
lib/host/uri.go
Normal file
82
lib/host/uri.go
Normal 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)
|
||||
}
|
3
main.go
3
main.go
@ -9,11 +9,12 @@ import (
|
||||
func main() {
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer venus.Close()
|
||||
log.Println(venus)
|
||||
lm, err := venus.GetGuestByName("logan-minecraft")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
9
util/util.go
Normal file
9
util/util.go
Normal 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"
|
||||
}
|
Loading…
Reference in New Issue
Block a user