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
|
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"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.staur.ca/stobbsm/clustvirt/util"
|
||||||
"libvirt.org/go/libvirt"
|
"libvirt.org/go/libvirt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -120,11 +121,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 +142,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 +156,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 +184,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 +211,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)
|
||||||
|
@ -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
|
package host
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"git.staur.ca/stobbsm/clustvirt/lib/guest"
|
"git.staur.ca/stobbsm/clustvirt/lib/guest"
|
||||||
|
"git.staur.ca/stobbsm/clustvirt/util"
|
||||||
"libvirt.org/go/libvirt"
|
"libvirt.org/go/libvirt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,17 +18,51 @@ import (
|
|||||||
// If a connection is closed prematurely, will re-open the connection and
|
// If a connection is closed prematurely, will re-open the connection and
|
||||||
// try the attempted method again
|
// try the attempted method again
|
||||||
type Host struct {
|
type Host struct {
|
||||||
HostName string
|
HostName string
|
||||||
|
SystemHomeName string
|
||||||
|
FreeMemory uint64
|
||||||
|
LibVersion uint32
|
||||||
|
HostInfo NodeInfo
|
||||||
|
HostSEVInfo SEVInfo
|
||||||
|
AvailableCPUTypes []string
|
||||||
|
|
||||||
|
uri *URI
|
||||||
conn *libvirt.Connect
|
conn *libvirt.Connect
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
closeErr chan error
|
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
|
// 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{
|
h := &Host{
|
||||||
HostName: host,
|
HostName: host,
|
||||||
|
uri: uri,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.connect(); err != nil {
|
if err := h.connect(); err != nil {
|
||||||
@ -32,6 +72,52 @@ func ConnectHost(host string) (*Host, error) {
|
|||||||
h.close = make(chan struct{})
|
h.close = make(chan struct{})
|
||||||
h.closeErr = make(chan error)
|
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() {
|
go func() {
|
||||||
defer close(h.closeErr)
|
defer close(h.closeErr)
|
||||||
<-h.close
|
<-h.close
|
||||||
@ -44,9 +130,7 @@ func ConnectHost(host string) (*Host, error) {
|
|||||||
// connect creates a host connection
|
// connect creates a host connection
|
||||||
func (h *Host) connect() error {
|
func (h *Host) connect() error {
|
||||||
var err error
|
var err error
|
||||||
h.conn, err = libvirt.NewConnect(
|
h.conn, err = libvirt.NewConnect(h.uri.ConnectionString(h.HostName))
|
||||||
fmt.Sprintf("qemu+ssh://%s/system", h.HostName),
|
|
||||||
)
|
|
||||||
return err
|
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() {
|
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()
|
||||||
|
log.Println(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)
|
||||||
|
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