diff --git a/go.mod b/go.mod index 74b3fd3..b90e8f5 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,9 @@ go 1.22.1 require ( github.com/a-h/templ v0.2.598 github.com/go-chi/chi/v5 v5.0.12 + github.com/jaypipes/pcidb v1.0.0 github.com/wcharczuk/go-chart/v2 v2.1.1 - gopkg.in/ffmt.v1 v1.5.6 + golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 libvirt.org/go/libvirt v1.10001.0 libvirt.org/go/libvirtxml v1.10001.0 ) @@ -14,7 +15,6 @@ 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 ae71f17..46b28d1 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/wcharczuk/go-chart/v2 v2.1.1/go.mod h1:CyCAUt2oqvfhCl6Q5ZvAZwItgpQKZO github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc= +golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -47,8 +49,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/ffmt.v1 v1.5.6 h1:4Bu3riZp5sAIXW2T/18JM9BkwJLodurXFR0f7PXp+cw= -gopkg.in/ffmt.v1 v1.5.6/go.mod h1:LssvGOZFiBGoBcobkTqnyh+uN1VzIRoibW+c0JI/Ha4= libvirt.org/go/libvirt v1.10001.0 h1:lEVDNE7xfzmZXiDEGIS8NvJSuaz11OjRXw+ufbQEtPY= libvirt.org/go/libvirt v1.10001.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= libvirt.org/go/libvirtxml v1.10001.0 h1:r9WBs24r3mxIG3/hAMRRwDMy4ZaPHmhHjw72o/ceXic= diff --git a/lib/host/lib.go b/lib/host/lib.go index 62db518..062ef0a 100644 --- a/lib/host/lib.go +++ b/lib/host/lib.go @@ -11,9 +11,6 @@ import ( "sync" "git.staur.ca/stobbsm/clustvirt/lib/guest" - "git.staur.ca/stobbsm/clustvirt/lib/secret" - "git.staur.ca/stobbsm/clustvirt/lib/storagepool" - "git.staur.ca/stobbsm/clustvirt/lib/storagevol" "libvirt.org/go/libvirt" "libvirt.org/go/libvirtxml" ) @@ -37,23 +34,23 @@ type Host struct { // Secure indicates if the connection is secure Secure bool // HostInfo provides basic HW information about a host - HostInfo libvirtxml.CapsHost + HostInfo *libvirtxml.CapsHost // NodeMemory provides basic memory information about the Host - NodeMemory NodeMemoryInfo + NodeMemory *NodeMemoryInfo // VMList is the list of virtual machines available to the host - VMList []libvirtxml.Domain + VMList []*libvirtxml.Domain // NetIfList is the list of network interfaces on the host - NetIfFList []libvirtxml.Interface + NetIfFList []*libvirtxml.Interface // NetworkList is the list of defined networks on the host - NetworkList []libvirtxml.Network + NetworkList []*libvirtxml.Network // DeviceList is the list of devices on the host - DeviceList []libvirtxml.NodeDevice + DeviceList []*libvirtxml.NodeDevice // SecretList provides a list of secrets available to the host - SecretList []libvirtxml.Secret + SecretList []*libvirtxml.Secret // StoragePoolList provides the list of stoarge ppols available to the host - StoragePoolList []libvirtxml.StoragePool + StoragePoolList []*libvirtxml.StoragePool // VolumeList is the list of volumes available on the host - VolumeList []libvirtxml.StorageVolume + VolumeList []*libvirtxml.StorageVolume uri *URI conn *libvirt.Connect @@ -128,13 +125,14 @@ func (h *Host) getInfo() { wg := new(sync.WaitGroup) infoFuncs := []func(){ - h.getDevicesInfo, - // h.getDomainInfo, - h.getIfaceInfo, + h.hostInfo, + h.memoryInfo, + h.getStoragePools, h.getNetsInfo, - h.getNodeInfo, - // h.getSecretsInfo, - // h.getStoragePools, + h.getIfaceInfo, + h.getDevicesInfo, + h.getDomainInfo, + h.getSecretsInfo, } for _, f := range infoFuncs { @@ -148,159 +146,40 @@ func (h *Host) getInfo() { wg.Wait() } -func (h *Host) getStoragePools() { - spools, err := h.conn.ListAllStoragePools(0) - if err != nil { - log.Println(err) - } - if len(spools) > 0 { - h.StoragePoolList = make([]StoragePoolInfo, len(spools)) - for i, s := range spools { - if h.StoragePoolList[i].XML, err = s.GetXMLDesc(0); err != nil { - log.Println(err) - } - if h.StoragePoolList[i].Name, err = s.GetName(); err != nil { - log.Println(err) - } - if h.StoragePoolList[i].UUID, err = s.GetUUID(); err != nil { - log.Println(err) - } - if h.StoragePoolList[i].Active, err = s.IsActive(); err != nil { - log.Println(err) - } - if h.StoragePoolList[i].Persistent, err = s.IsPersistent(); err != nil { - log.Println(err) - } - if h.StoragePoolList[i].AutoStart, err = s.GetAutostart(); err != nil { - log.Println(err) - } - spInfo, err := s.GetInfo() - if err != nil { - log.Println(err) - } - h.StoragePoolList[i].State = storagepool.StoragePoolStateMap[spInfo.State] - h.StoragePoolList[i].Capacity = spInfo.Capacity - h.StoragePoolList[i].Allocation = spInfo.Allocation - h.StoragePoolList[i].Available = spInfo.Available - - spoolXML := &libvirtxml.StoragePool{} - if err = spoolXML.Unmarshal(h.StoragePoolList[i].XML); err != nil { - 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 - } - } - - svols, err := s.ListAllStorageVolumes(0) - if err != nil { - log.Println(err) - } - if len(svols) > 0 { - h.StoragePoolList[i].Volumes = make([]VolumeInfo, len(svols)) - for j, sv := range svols { - if h.StoragePoolList[i].Volumes[j].Name, err = sv.GetName(); err != nil { - log.Println(err) - } - if h.StoragePoolList[i].Volumes[j].Key, err = sv.GetKey(); err != nil { - log.Println(err) - } - if h.StoragePoolList[i].Volumes[j].Path, err = sv.GetPath(); err != nil { - log.Println(err) - } - svInfo, err := sv.GetInfo() // Type, Capacity, Allocation - if err != nil { - log.Println(err) - } - h.StoragePoolList[i].Volumes[j].Type = storagevol.StorageVolTypeMap[svInfo.Type] - h.StoragePoolList[i].Volumes[j].Capacity = svInfo.Capacity - h.StoragePoolList[i].Volumes[j].Allocation = svInfo.Allocation - if h.StoragePoolList[i].Volumes[j].XML, err = sv.GetXMLDesc(0); err != nil { - log.Println(err) - } - - sv.Free() - } - } - - s.Free() - } - } -} - -func (h *Host) getSecretsInfo() { - nsecrets, err := h.conn.ListAllSecrets(0) - if err != nil { - log.Println(err) - } - if len(nsecrets) > 0 { - h.SecretList = make([]SecretInfo, len(nsecrets)) - for i, s := range nsecrets { - if h.SecretList[i].XML, err = s.GetXMLDesc(0); err != nil { - log.Println(err) - } - stype, err := s.GetUsageType() - if err != nil { - log.Println(err) - } - h.SecretList[i].Type = secret.SecretUsageTypeMap[stype] - - s.Free() - } - } -} - -func (h *Host) getNodeInfo() { +func (h *Host) hostInfo() { var err error - h.AvailableCPUTypes, err = h.conn.GetCPUModelNames("x86_64", 0) - if err != nil { - log.Println("Error getting cpu model names", err) - } - ni, err := h.conn.GetNodeInfo() + rawxml, err := h.conn.GetCapabilities() if err != nil { - log.Println(err) + log.Printf("error getting host capabilities XML: %s", 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 + xmldoc := &libvirtxml.Caps{} + if err = xmldoc.Unmarshal(rawxml); err != nil { + log.Printf("error parsing host capabilities XML: %s", err) + } + h.HostInfo = &xmldoc.Host h.SystemHostName, err = h.conn.GetHostname() if err != nil { - log.Println(err) - } - if h.SystemHostName == "" { + log.Println("error getting system host name: %s", err) + } else { h.SystemHostName = h.HostName } h.LibVersion, err = h.conn.GetLibVersion() if err != nil { log.Println(err) } - h.FreeMemory, err = h.conn.GetFreeMemory() - if err != nil { - log.Println(err) - } +} +func (h *Host) memoryInfo() { mi, err := h.conn.GetMemoryStats(libvirt.NODE_MEMORY_STATS_ALL_CELLS, 0) if err != nil { log.Println(err) } - h.NodeMemory.Total = mi.Total - h.NodeMemory.Free = mi.Free - h.NodeMemory.Buffers = mi.Buffers - h.NodeMemory.Cached = mi.Cached - - h.StorageCapabilities, err = h.conn.GetStoragePoolCapabilities(0) - if err != nil { - log.Println(err) + h.NodeMemory = &NodeMemoryInfo{ + Total: mi.Total, + Free: mi.Free, + Buffers: mi.Buffers, + Cached: mi.Cached, } h.SysInfo, err = h.conn.GetSysinfo(0) @@ -322,6 +201,101 @@ func (h *Host) getNodeInfo() { } } +func (h *Host) getStoragePools() { + // Get list of all storage pools on the host + spools, err := h.conn.ListAllStoragePools(0) + if err != nil { + log.Println(err) + } + if len(spools) > 0 { + h.StoragePoolList = make([]*libvirtxml.StoragePool, len(spools)) + for i, s := range spools { + // run in a function to allow defer s.Free() + func() { + defer s.Free() + // Get the XML represenation of each storage pool, parse it with libvirtxml + rawxml, err := s.GetXMLDesc(0) + if err != nil { + log.Println("error getting storage pool xml: %s", err) + return + } + xmldoc := &libvirtxml.StoragePool{} + err = xmldoc.Unmarshal(rawxml) + if err != nil { + log.Println("error parsing storage pool XML: %s", err) + return + } + h.StoragePoolList[i] = xmldoc + + // Get list of all storage volumes in the current storage pool + svols, err := s.ListAllStorageVolumes(0) + if err != nil { + log.Println(err) + } + if len(svols) > 0 { + // define temporary variable to hold slice of StorageVolume, that can + // either be appended to the existing slice, or used in place of the newly + // defined slice + tvl := make([]*libvirtxml.StorageVolume, len(svols)) + for j, sv := range svols { + // run in a function so I can defer the sv.Free() call + func() { + defer sv.Free() + rawxml, err = sv.GetXMLDesc(0) + if err != nil { + log.Printf("error getting XML from storage volume: %s", err) + return + } + xmldoc := &libvirtxml.StorageVolume{} + err = xmldoc.Unmarshal(rawxml) + if err != nil { + log.Printf("error parsing storage volume XML: %s", err) + return + } + + tvl[j] = xmldoc + }() + } + // Append the contents of tvl to h.VolumeList + if h.VolumeList == nil { + h.VolumeList = []*libvirtxml.StorageVolume{} + } + // Only append if the temporary storage volume isn't nil + for _, tsv := range tvl { + if tsv != nil { + h.VolumeList = append(h.VolumeList, tsv) + } + } + } + }() + } + } +} + +func (h *Host) getSecretsInfo() { + nsecrets, err := h.conn.ListAllSecrets(0) + if err != nil { + log.Printf("error loading secrets from host: %s", err) + } + if len(nsecrets) > 0 { + h.SecretList = make([]*libvirtxml.Secret, len(nsecrets)) + for i, s := range nsecrets { + func() { + defer s.Free() + rawxml, err := s.GetXMLDesc(0) + if err != nil { + log.Printf("error getting secret XML", err) + } + xmldoc := &libvirtxml.Secret{} + if err = xmldoc.Unmarshal(rawxml); err != nil { + log.Printf("error parsing secret XML: %s", err) + } + h.SecretList[i] = xmldoc + }() + } + } +} + func (h *Host) getDomainInfo() { // getDomainInfo doms, err := h.conn.ListAllDomains(0)