diff --git a/cluster/lock/lock.go b/cluster/lock/lock.go
new file mode 100644
index 0000000..ee8755a
--- /dev/null
+++ b/cluster/lock/lock.go
@@ -0,0 +1,19 @@
+// Package lock implements a locking mechanism on shared resources to ensure
+// they don't get used at the same time. There needs to be a lock for the following:
+// - VM on Host: A VM must only exist on one host at a time
+// - Storage attached to VM: Block storage can only be attached to one VM at a time
+package lock
+
+// Locker interface used to lock and unlock Lockable resources
+type Locker interface {
+ Lock(Lockable) error
+ Unlock(Lockable) error
+}
+
+// Lockable interface must be attached to lockable resources, such as
+// Virtual Machines, block devices, and host devices that can be attached
+// to virtual machines.
+type Lockable interface {
+ Locked() bool
+ HeldBy() Locker
+}
diff --git a/cluster/lock/volume.go b/cluster/lock/volume.go
new file mode 100644
index 0000000..3716883
--- /dev/null
+++ b/cluster/lock/volume.go
@@ -0,0 +1 @@
+package lock
diff --git a/cluster/stats.go b/cluster/stats.go
index 672fc30..3c755dc 100644
--- a/cluster/stats.go
+++ b/cluster/stats.go
@@ -1,96 +1,168 @@
package cluster
// ClusterStats is used to gather stats for the entire cluster
+// Combined with StatsDiff, we can get some basic cluster wide stats tracking
type ClusterStats struct {
- CPU struct {
- Sockets uint32
- Cores uint32
- Threads uint32
- Allocated uint32
- }
- Memory struct {
- Total uint64
- Free uint64
- Buffers uint64
- Cached uint64
- Allocated uint64
- }
- Storage struct {
- Total uint64
- Used uint64
- Free uint64
- Active uint32
- Inactive uint32
- Pools uint32
-
- Volumes struct {
- Total uint32
- Active uint32
- Inactive uint32
- }
- }
- VM struct {
- Count uint32
- Started uint32
- Stopped uint32
- }
- Host struct {
- Count uint32
- Available uint32
- }
- Network struct {
- Count uint32
- Active uint32
- Inactive uint32
- }
+ // CPU Statistics including number of CPUs
+ CPU CPUStats
+ // Memory provides information about the amount of memory, including free and
+ // allocated memory
+ Memory MemoryStats
+ // Storage provides information about storage pools, Only get's stats for active
+ // pools, and will not activate pools that are not already active.
+ // Trys to sort out shared file systems from local filesystems using the Type parameter
+ // of Host.StoragePoolInfo
+ Storage StorageStats
+ // Volume provides information on allocated volumes used in the cluster
+ Volume VolumeStats
+ // VM provides VM specific counters for the cluster
+ VM VMStats
+ // Host provides Host information for the cluster
+ Host HostStats
+ // Network provices available networks, and how many are shared between hosts
+ Network NetworkStats
+ // NetIF provides information about Libvirt allocated networks, usable by the
+ // libvirt cluster
+ NetIF NetIFStats
old *ClusterStats
c *Cluster
}
+// CPUStats provides information about the number of CPUs, Cores,
+// Threads, and Speed available to the cluster.
+type CPUStats struct {
+ Sockets uint32
+ Cores uint32
+ Threads uint32
+ Allocated uint32
+ MHz uint64
+}
+
+// MemoryStats provies information about the amount of memory, including free and
+// allocated memory. Allocated is the total allocated to Guests
+type MemoryStats struct {
+ Total uint64
+ Free uint64
+ Buffers uint64
+ Cached uint64
+ Allocated uint64
+}
+
+// StorageStats provides information about the available storage pools in the cluster,
+// including the amount of space available, allocated, and how many pools are shared
+// between hosts
+type StorageStats struct {
+ Total uint64
+ Used uint64
+ Free uint64
+ Active uint32
+ Inactive uint32
+ Pools uint32
+}
+
+// VolumeStats provides information about the number of volumes on the cluster.
+// Counts volumes in shared storage (as detmermined by StorageStats) only once
+type VolumeStats struct {
+ Total uint32
+ Active uint32
+ Inactive uint32
+}
+
+// VMStats provides information about the defined Virtual Machines on the cluster
+type VMStats struct {
+ Count uint32
+ Started uint32
+ Stopped uint32
+}
+
+// HostStats provides informatoin about the number of hosts defined, and how many
+// are currently available. An unavailable host will not have it's statistics counted
+type HostStats struct {
+ Count uint32
+ Available uint32
+ Nodes uint32
+}
+
+// NetworkStats provides informatoin about the available Host network connections,
+// including bridges and ethernet devices.
+type NetworkStats struct {
+ Count uint32
+ Active uint32
+ Inactive uint32
+}
+
+// NetIFStats provides information about Libvirt defined networks
+type NetIFStats struct {
+ Count uint32
+ Common uint32
+ Active uint32
+ Inactive uint32
+}
+
+// DeviceStats provides information about the number of allocatable devices in the
+// cluster. These are PCI and USB devices.
+type DeviceStats struct {
+ Count uint32
+}
+
+// SecretStats provides the number of secrets defined throughout the cluster.
+// Shared secrets are only counted once, and are recognized by their UUID
+type SecretStats struct {
+ Count uint32
+ Shared uint32
+}
+
// ClusterStats is used to gather stats for the entire cluster
type StatDiff struct {
- CPU struct {
- Sockets int
- Cores int
- Threads int
- Allocated int
- }
- Memory struct {
- Total int
- Free int
- Buffers int
- Cached int
- Allocated int
- }
- Storage struct {
- Total int
- Used int
- Free int
- Active int
- Inactive int
- Pools int
+ CPU CPUDiff
+ Memory MemoryDiff
+ Storage StorageStats
+ Volume VolumeDiff
+ VM VMDiff
+ Host HostDiff
+ Network NetworkDiff
+}
- Volumes struct {
- Total int
- Active int
- Inactive int
- }
- }
- VM struct {
- Count int
- Started int
- Stopped int
- }
- Host struct {
- Count int
- Available int
- }
- Network struct {
- Count int
- Active int
- Inactive int
- }
+type CPUDiff struct {
+ Sockets int
+ Cores int
+ Threads int
+ Allocated int
+}
+type MemoryDiff struct {
+ Total int64
+ Free int64
+ Buffers int64
+ Cached int64
+ Allocated int64
+}
+type StorageDiff struct {
+ Total int64
+ Used int64
+ Free int64
+ Active int64
+ Inactive int64
+ Pools int
+}
+type VolumeDiff struct {
+ Total int
+ Active int
+ Inactive int
+}
+type VMDiff struct {
+ Count int
+ Started int
+ Stopped int
+}
+type HostDiff struct {
+ Count int
+ Available int
+}
+type NetworkDiff struct {
+ Count int
+ Active int
+ Inactive int
}
// InitStats is given a cluster, which it then uses to load the initial statistics
@@ -144,9 +216,9 @@ func (cs *ClusterStats) Update() {
cs.Storage.Used += sp.Allocation
cs.Storage.Free += sp.Capacity - sp.Allocation
// Volumes in the pool
- cs.Storage.Volumes.Total += uint32(len(sp.Volumes))
+ cs.Volume.Total += uint32(len(sp.Volumes))
for range sp.Volumes {
- cs.Storage.Volumes.Active++
+ cs.Volume.Active++
}
}
@@ -225,9 +297,9 @@ func (cs *ClusterStats) Diff() StatDiff {
Active int
Inactive int
}{
- Total: int(cs.old.Storage.Volumes.Total - cs.Storage.Volumes.Total),
- Active: int(cs.old.Storage.Volumes.Active - cs.Storage.Volumes.Active),
- Inactive: int(cs.old.Storage.Volumes.Inactive - cs.Storage.Volumes.Inactive),
+ Total: int(cs.old.Volume.Total - cs.Volume.Total),
+ Active: int(cs.old.Volume.Active - cs.Volume.Active),
+ Inactive: int(cs.old.Volume.Inactive - cs.Volume.Inactive),
},
},
VM: struct {
diff --git a/go.mod b/go.mod
index 5595d94..74b3fd3 100644
--- a/go.mod
+++ b/go.mod
@@ -14,5 +14,7 @@ require (
require (
github.com/blend/go-sdk v1.20220411.3 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
+ github.com/jaypipes/pcidb v1.0.0 // indirect
+ github.com/mitchellh/go-homedir v1.0.0 // indirect
golang.org/x/image v0.11.0 // indirect
)
diff --git a/go.sum b/go.sum
index 13dfa44..ae71f17 100644
--- a/go.sum
+++ b/go.sum
@@ -8,6 +8,10 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/jaypipes/pcidb v1.0.0 h1:vtZIfkiCUE42oYbJS0TAq9XSfSmcsgo9IdxSm9qzYU8=
+github.com/jaypipes/pcidb v1.0.0/go.mod h1:TnYUvqhPBzCKnH34KrIX22kAeEbDCSRJ9cqLRCuNDfk=
+github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/wcharczuk/go-chart/v2 v2.1.1 h1:2u7na789qiD5WzccZsFz4MJWOJP72G+2kUuJoSNqWnE=
github.com/wcharczuk/go-chart/v2 v2.1.1/go.mod h1:CyCAUt2oqvfhCl6Q5ZvAZwItgpQKZOkCJGb+VGv6l14=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
diff --git a/lib/host/lib.go b/lib/host/lib.go
index 4ef75f0..62db518 100644
--- a/lib/host/lib.go
+++ b/lib/host/lib.go
@@ -14,7 +14,6 @@ import (
"git.staur.ca/stobbsm/clustvirt/lib/secret"
"git.staur.ca/stobbsm/clustvirt/lib/storagepool"
"git.staur.ca/stobbsm/clustvirt/lib/storagevol"
- "git.staur.ca/stobbsm/clustvirt/util"
"libvirt.org/go/libvirt"
"libvirt.org/go/libvirtxml"
)
@@ -27,20 +26,8 @@ type Host struct {
HostName string
// SystemHostName is the hostname as reported by the system itself
SystemHostName string
- // FreeMemory is the available free memory
- FreeMemory uint64
// LibVersion is the version of Libvirt on the host
LibVersion uint32
- // HostInfo provides basic HW information about a host
- HostInfo NodeInfo
- // HostSEVInfo provides informatoin about AMD SEV extentions available on the host
- HostSEVInfo SEVInfo
- // AvailableCPUTypes are the available types of CPUs that can be used for VM creation
- AvailableCPUTypes []string
- // NodeMemory provides basic memory information about the Host
- NodeMemory NodeMemoryInfo
- // StorageCapabilities is the XML representation of the hosts storage capabilities
- StorageCapabilities string
// SysInfo is the XML representation of the host system information
SysInfo string
// Alive indicates if the connection is alive
@@ -49,18 +36,24 @@ type Host struct {
Encrypted bool
// Secure indicates if the connection is secure
Secure bool
+ // HostInfo provides basic HW information about a host
+ HostInfo libvirtxml.CapsHost
+ // NodeMemory provides basic memory information about the Host
+ NodeMemory NodeMemoryInfo
// VMList is the list of virtual machines available to the host
- VMList []VMInfo
+ VMList []libvirtxml.Domain
// NetIfList is the list of network interfaces on the host
- NetIfFList []NetIfInfo
+ NetIfFList []libvirtxml.Interface
// NetworkList is the list of defined networks on the host
- NetworkList []NetworkInfo
+ NetworkList []libvirtxml.Network
// DeviceList is the list of devices on the host
- DeviceList []DeviceInfo
+ DeviceList []libvirtxml.NodeDevice
// SecretList provides a list of secrets available to the host
- SecretList []SecretInfo
+ SecretList []libvirtxml.Secret
// StoragePoolList provides the list of stoarge ppols available to the host
- StoragePoolList []StoragePoolInfo
+ StoragePoolList []libvirtxml.StoragePool
+ // VolumeList is the list of volumes available on the host
+ VolumeList []libvirtxml.StorageVolume
uri *URI
conn *libvirt.Connect
@@ -68,108 +61,6 @@ type Host struct {
closeErr chan error
}
-// DeviceInfo holds basic information for host devices
-type DeviceInfo struct {
- Name string
- Capabilities []string
- XML string
-}
-
-// VMInfo holds basic VM information, like the name and ID
-type VMInfo struct {
- Name string
- ID uint
- UUID []byte
- XML string
- Active bool
- VCPUs uint
- Memory uint
- // States are the current states active on the host
- States []guest.VMState
-}
-
-// SecretInfo doesn't let you see the contents of secrets, but does let you see what secrets have
-// been defined in a simple format.
-type SecretInfo struct {
- UUID string
- XML string
- Type string
-}
-
-// StoragePoolInfo holds basic information on storage pools
-type StoragePoolInfo struct {
- Name string
- UUID []byte
- Type string
- XML string
- Active bool
- Persistent bool
- AutoStart bool
- State string
- Capacity uint64
- Allocation uint64
- Available uint64
- IsNet bool
- // HAEnabled indicates if the storage pool has High Availability
- HAEnabled bool
- // Volumes defined in the storage pool
- Volumes []VolumeInfo
-}
-
-// VolumeInfo holds basic information about Volumes available in storage pools
-type VolumeInfo struct {
- Name string
- Key string
- Path string
- Type string
- Capacity uint64
- Allocation uint64
- XML string
-}
-
-// NetIfInfo holds basic information about available network interfaces (not their connections, the devices themselves)
-type NetIfInfo struct {
- Name string
- MacAddr string
- XML string
-}
-
-// NetworkInfo holds basic information about network connections
-type NetworkInfo struct {
- Name string
- UUID []byte
- XML string
- Active bool
- // NetIf is the network interface this connection is applied to
- NetIf NetIfInfo
-}
-
-// 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
-}
-
// NodeMemoryInfo provides statistis about node memory usage from libvirt.NodeMemoryStats
type NodeMemoryInfo struct {
Total uint64
@@ -234,7 +125,7 @@ func (h *Host) Close() error {
// private methods that load the different informational parts
func (h *Host) getInfo() {
- var wg = new(sync.WaitGroup)
+ wg := new(sync.WaitGroup)
infoFuncs := []func(){
h.getDevicesInfo,
@@ -242,7 +133,6 @@ func (h *Host) getInfo() {
h.getIfaceInfo,
h.getNetsInfo,
h.getNodeInfo,
- // h.getSEVInfo,
// h.getSecretsInfo,
// h.getStoragePools,
}
@@ -298,13 +188,13 @@ func (h *Host) getStoragePools() {
log.Println(err)
}
h.StoragePoolList[i].Type = spoolXML.Type
- for _, t := range storagepool.NetTypes {
- if h.StoragePoolList[i].Type == t {
- h.StoragePoolList[i].IsNet = true
- h.StoragePoolList[i].HAEnabled = true
- continue
- }
- }
+ for _, t := range storagepool.NetTypes {
+ if h.StoragePoolList[i].Type == t {
+ h.StoragePoolList[i].IsNet = true
+ h.StoragePoolList[i].HAEnabled = true
+ continue
+ }
+ }
svols, err := s.ListAllStorageVolumes(0)
if err != nil {
@@ -432,34 +322,6 @@ func (h *Host) getNodeInfo() {
}
}
-func (h *Host) getSEVInfo() {
- // getSEVInfo
- h.HostSEVInfo.SEVEnabled = true
- ns, err := h.conn.GetSEVInfo(0)
- if err != nil {
- log.Println(err)
- lverr, ok := err.(libvirt.Error)
- if ok {
- 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)
- }
-}
-
func (h *Host) getDomainInfo() {
// getDomainInfo
doms, err := h.conn.ListAllDomains(0)
@@ -482,15 +344,15 @@ func (h *Host) getDomainInfo() {
if h.VMList[i].XML, err = d.GetXMLDesc(0); err != nil {
log.Println(err)
}
- vmXML := &libvirtxml.Domain{}
- if err = vmXML.Unmarshal(h.VMList[i].XML); err != nil {
- log.Println(err)
- }
- h.VMList[i].VCPUs = vmXML.VCPU.Value
- h.VMList[i].Memory = vmXML.CurrentMemory.Value
- if h.VMList[i].Active, err = d.IsActive(); err != nil {
- log.Println(err)
- }
+ vmXML := &libvirtxml.Domain{}
+ if err = vmXML.Unmarshal(h.VMList[i].XML); err != nil {
+ log.Println(err)
+ }
+ h.VMList[i].VCPUs = vmXML.VCPU.Value
+ h.VMList[i].Memory = vmXML.CurrentMemory.Value
+ if h.VMList[i].Active, err = d.IsActive(); err != nil {
+ log.Println(err)
+ }
d.Free()
}
@@ -545,9 +407,9 @@ func (h *Host) getNetsInfo() {
if h.NetworkList[i].XML, err = net.GetXMLDesc(0); err != nil {
log.Println(err)
}
- if h.NetworkList[i].Active, err = net.IsActive(); err != nil {
- log.Println(err)
- }
+ if h.NetworkList[i].Active, err = net.IsActive(); err != nil {
+ log.Println(err)
+ }
net.Free()
}
@@ -571,6 +433,12 @@ func (h *Host) getDevicesInfo() {
if h.DeviceList[i].XML, err = dev.GetXMLDesc(0); err != nil {
log.Println(err)
}
+ dx := &libvirtxml.NodeDevice{}
+ if err != dx.Unmarshal(h.DeviceList[i].XML); err != nil {
+ log.Println(err)
+ }
+ h.DeviceList[i].Driver = dx.Driver.Name
+ dx.Capability.PCI.Class
dev.Free()
}
diff --git a/util/pcidb.go b/util/pcidb.go
new file mode 100644
index 0000000..c764d1c
--- /dev/null
+++ b/util/pcidb.go
@@ -0,0 +1,48 @@
+package util
+
+import (
+ "log"
+
+ "github.com/jaypipes/pcidb"
+)
+
+var (
+ pcidbInitDone = false
+ db *pcidb.PCIDB
+)
+
+const (
+ pcidbNOTFOUND string = `NOTFOUND`
+ pcidbNODB string = `NODBFOUND`
+)
+
+func initPCIDB() {
+ var err error
+
+ // Attempt to use local sources first, fallback to network if
+ // local sources aren't found
+ db, err = pcidb.New()
+ if err != nil {
+ log.Printf("warning: couldn't use local pcidb cache: %s", err)
+ log.Println("falling back to downloading database")
+ db, err = pcidb.New(pcidb.WithEnableNetworkFetch())
+ if err != nil {
+ log.Println("error: couldn't get pcidb. no more fallbacks available, will not be able to query the pcidb")
+ }
+ }
+ pcidbInitDone = true
+}
+
+func GetPCIClass(id string) string {
+ if !pcidbInitDone {
+ initPCIDB()
+ }
+ if pcidbInitDone && db == nil {
+ log.Println("unable to access pcidb")
+ return pcidbNODB
+ }
+ if class, ok := db.Classes[id]; ok {
+ return class.Name
+ }
+ return pcidbNOTFOUND
+}
diff --git a/view/cluster.templ b/view/cluster.templ
index fcf0904..2ceaa40 100644
--- a/view/cluster.templ
+++ b/view/cluster.templ
@@ -10,48 +10,58 @@ import (
templ ClusterInfo(cs *cluster.ClusterStats, diff cluster.StatDiff, navbar []components.NavItem) {
@layouts.Manager("ClustVirt", "Cluster Manager", navbar) {
Cluster Stats
-
-
- CPU stats
-
-
-
- Sockets |
- Cores |
- Threads |
- Allocated |
-
-
-
-
-
- { fmt.Sprint(cs.CPU.Sockets) }
- |
-
- { fmt.Sprint(cs.CPU.Cores) }
- |
-
- { fmt.Sprint(cs.CPU.Threads) }
- |
-
- { fmt.Sprint(cs.CPU.Allocated) }
- |
-
-
-
- { fmt.Sprint(diff.CPU.Sockets) }
- |
-
- { fmt.Sprint(diff.CPU.Cores) }
- |
-
- { fmt.Sprint(diff.CPU.Threads) }
- |
-
- { fmt.Sprint(diff.CPU.Allocated) }
- |
-
-
-
}
}
+
+templ CPUStats() {
+
+
+ CPU stats
+
+
+
+ |
+ Sockets |
+ Cores |
+ Threads |
+ Allocated |
+
+
+
+
+
+ Latest
+ |
+
+ { fmt.Sprint(cs.CPU.Sockets) }
+ |
+
+ { fmt.Sprint(cs.CPU.Cores) }
+ |
+
+ { fmt.Sprint(cs.CPU.Threads) }
+ |
+
+ { fmt.Sprint(cs.CPU.Allocated) }
+ |
+
+
+
+ Change
+ |
+
+ { fmt.Sprint(diff.CPU.Sockets) }
+ |
+
+ { fmt.Sprint(diff.CPU.Cores) }
+ |
+
+ { fmt.Sprint(diff.CPU.Threads) }
+ |
+
+ { fmt.Sprint(diff.CPU.Allocated) }
+ |
+
+
+
+}