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 -
SocketsCoresThreadsAllocated
- { 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 +
SocketsCoresThreadsAllocated
+ 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) } +
+}