diff --git a/cluster/cluster.go b/cluster/cluster.go index d11cc9b..c3334d4 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -9,6 +9,7 @@ package cluster import ( + "fmt" "time" "git.staur.ca/stobbsm/clustvirt/lib/host" @@ -24,3 +25,10 @@ type Cluster struct { hosts map[string]*host.Host defaultURI *host.URI } + +func (c *Cluster) GetHost(h string) (*host.Host, error) { + if hh, ok := c.hosts[h]; ok { + return hh, nil + } + return nil, fmt.Errorf("%s: %w", h, ErrHostNotFound) +} diff --git a/cluster/errors.go b/cluster/errors.go new file mode 100644 index 0000000..5db7626 --- /dev/null +++ b/cluster/errors.go @@ -0,0 +1,8 @@ +package cluster + +import "errors" + +// Errors that can happen in the cluster +var ( + ErrHostNotFound = errors.New("host not found") +) diff --git a/cluster/stats.go b/cluster/stats.go index ef9aefe..d004c60 100644 --- a/cluster/stats.go +++ b/cluster/stats.go @@ -82,7 +82,6 @@ type VMStats struct { // 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 } @@ -132,6 +131,7 @@ type CPUDiff struct { Cores int Threads int Allocated int + MHz int } type MemoryDiff struct { Total int64 @@ -186,12 +186,13 @@ func (cs *ClusterStats) Update() { cs.reset() // Start looping through each host in the cluster, adding to the total for _, h := range cs.c.hosts { - cs.Host.Count++ + cs.Host.Nodes++ cs.Host.Available++ cs.CPU.Sockets += uint32(h.HostInfo.CPU.Topology.Sockets) cs.CPU.Cores += uint32(h.HostInfo.CPU.Topology.Cores) cs.CPU.Threads += uint32(h.HostInfo.CPU.Topology.Threads) + cs.CPU.MHz += uint64(h.HostInfo.CPU.Counter.Frequency) cs.Memory.Total += h.NodeMemory.Total cs.Memory.Free += h.NodeMemory.Free @@ -215,7 +216,7 @@ func (cs *ClusterStats) Update() { cs.Storage.Used += sp.Allocation.Value cs.Storage.Free += sp.Capacity.Value - sp.Allocation.Value } - + cs.Volume.Total += uint32(len(h.VolumeList)) // VM Count cs.VM.Count += uint32(len(h.VMList)) for _, vm := range h.VMList { @@ -237,6 +238,7 @@ func (cs *ClusterStats) Diff() StatDiff { Cores: int(cs.CPU.Cores) - int(cs.old.CPU.Cores), Threads: int(cs.CPU.Threads) - int(cs.old.CPU.Threads), Allocated: int(cs.CPU.Allocated) - int(cs.old.CPU.Allocated), + MHz: int(cs.CPU.MHz) - int(cs.old.CPU.MHz), }, Memory: MemoryDiff{ Total: int64(cs.old.Memory.Total) - int64(cs.Memory.Total), @@ -265,7 +267,7 @@ func (cs *ClusterStats) Diff() StatDiff { Stopped: int(cs.old.VM.Stopped) - int(cs.VM.Stopped), }, Host: HostDiff{ - Count: int(cs.old.Host.Count) - int(cs.Host.Count), + Count: int(cs.old.Host.Nodes) - int(cs.Host.Nodes), Available: int(cs.old.Host.Available) - int(cs.Host.Available), }, Network: NetworkDiff{ @@ -288,11 +290,13 @@ func (cs *ClusterStats) reset() { cs.CPU.Cores = 0 cs.CPU.Threads = 0 cs.CPU.Allocated = 0 + cs.CPU.MHz = 0 cs.Memory.Total = 0 cs.Memory.Free = 0 cs.Memory.Buffers = 0 cs.Memory.Cached = 0 + cs.Memory.Allocated = 0 cs.Storage.Total = 0 cs.Storage.Used = 0 @@ -301,16 +305,25 @@ func (cs *ClusterStats) reset() { cs.Storage.Inactive = 0 cs.Storage.Pools = 0 + cs.Volume.Total = 0 + cs.Volume.Active = 0 + cs.Volume.Inactive = 0 + cs.VM.Count = 0 cs.VM.Started = 0 cs.VM.Stopped = 0 - cs.Host.Count = 0 cs.Host.Available = 0 + cs.Host.Nodes = 0 cs.Network.Count = 0 cs.Network.Active = 0 cs.Network.Inactive = 0 + + cs.NetIF.Count = 0 + cs.NetIF.Common = 0 + cs.NetIF.Active = 0 + cs.NetIF.Inactive = 0 } func isNetworkPool(pooltype string) bool { diff --git a/go.mod b/go.mod index b3035a8..b80076e 100644 --- a/go.mod +++ b/go.mod @@ -12,8 +12,19 @@ require ( ) require ( + github.com/go-chi/cors v1.2.1 // indirect + github.com/go-chi/docgen v1.2.0 // indirect + github.com/go-chi/jwtauth v1.2.0 // indirect + github.com/goccy/go-json v0.3.5 // indirect + github.com/lestrrat-go/backoff/v2 v2.0.7 // indirect + github.com/lestrrat-go/httpcc v1.0.0 // indirect + github.com/lestrrat-go/iter v1.0.0 // indirect + github.com/lestrrat-go/jwx v1.1.0 // indirect + github.com/lestrrat-go/option v1.0.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect golang.org/x/sys v0.15.0 // indirect ) diff --git a/go.sum b/go.sum index 93f53f0..7364f28 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,38 @@ github.com/a-h/templ v0.2.598 h1:6jMIHv6wQZvdPxTuv87erW4RqN/FPU0wk7ZHN5wVuuo= github.com/a-h/templ v0.2.598/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= +github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= +github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-chi/docgen v1.2.0 h1:da0Nq2PKU9W9pSOTUfVrKI1vIgTGpauo9cfh4Iwivek= +github.com/go-chi/docgen v1.2.0/go.mod h1:G9W0G551cs2BFMSn/cnGwX+JBHEloAgo17MBhyrnhPI= +github.com/go-chi/jwtauth v1.2.0 h1:Z116SPpevIABBYsv8ih/AHYBHmd4EufKSKsLUnWdrTM= +github.com/go-chi/jwtauth v1.2.0/go.mod h1:NTUpKoTQV6o25UwYE6w/VaLUu83hzrVKYTVo+lE6qDA= +github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= +github.com/goccy/go-json v0.3.5 h1:HqrLjEWx7hD62JRhBh+mHv+rEEzBANIu6O0kbDlaLzU= +github.com/goccy/go-json v0.3.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 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/lestrrat-go/backoff/v2 v2.0.7 h1:i2SeK33aOFJlUNJZzf2IpXRBvqBBnaGXfY5Xaop/GsE= +github.com/lestrrat-go/backoff/v2 v2.0.7/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/codegen v1.0.0/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM= +github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc= +github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE= +github.com/lestrrat-go/iter v1.0.0 h1:QD+hHQPDSHC4rCJkZYY/yXChYr/vjfBopKekTc+7l4Q= +github.com/lestrrat-go/iter v1.0.0/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/jwx v1.1.0 h1:gerfaQK3mEIL8X8oJ5MFvsB/JuxXoGryLtTlNmPi3/k= +github.com/lestrrat-go/jwx v1.1.0/go.mod h1:vn9FzD6gJtKkgYs7RTKV7CjWtEka8F/voUollhnn4QE= +github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCiIqbJRmKeiADU4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -16,15 +41,55 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/main.go b/main.go index 3e9eeec..2a9c2b0 100644 --- a/main.go +++ b/main.go @@ -1,24 +1,12 @@ package main import ( - "context" - "fmt" "log" - "net/http" "git.staur.ca/stobbsm/clustvirt/cluster" - "git.staur.ca/stobbsm/clustvirt/lib/host" - "git.staur.ca/stobbsm/clustvirt/view" - "git.staur.ca/stobbsm/clustvirt/view/components" - "git.staur.ca/stobbsm/clustvirt/view/static" - "github.com/a-h/templ" - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" - localmw "git.staur.ca/stobbsm/clustvirt/router/middleware" + "git.staur.ca/stobbsm/clustvirt/router/server" ) -const DEBUG bool = true - func main() { log.Println("Starting clustvirt, the libvirt cluster manager") clst := cluster.New(). @@ -26,59 +14,8 @@ func main() { AddHost("venus.staur.ca"). AddHost("mars.staur.ca"). Build() - - initStats := cluster.InitStats(clst) // Start webserver and serve homepage - defaultNavBar := []components.NavItem{ - {Name: "Cluster", Href: "/"}, - {Name: "Configuration", Href: "/config"}, - {Name: "About", Href: "/about"}, - } - fs := http.StripPrefix("/static/", http.FileServer(http.Dir("public"))) - - r := chi.NewRouter() - r.Use(localmw.Logger) - if DEBUG { - r.Use(middleware.NoCache) - } - r.Get("/static/*", func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - fs.ServeHTTP(w, r) - }) - r.Get("/", templ.Handler(view.HostMain(defaultNavBar)).ServeHTTP) - r.Get("/cluster", func(w http.ResponseWriter, r *http.Request) { - initStats.Update() - log.Println( - "Rendering clusterstats", - view.ClusterInfo( - initStats, - initStats.Diff(), - defaultNavBar, - ).Render(context.Background(), w)) - }) - r.Get("/about", templ.Handler(static.Home()).ServeHTTP) - - r.Route("/htmx", func(r chi.Router) { - r.Get("/host/{hostname}", func(w http.ResponseWriter, r *http.Request) { - rhost, err := host.ConnectHost(host.URI_QEMU_SSH_SYSTEM, chi.URLParam(r, "hostname")) - if err != nil { - http.Error(w, fmt.Sprintf("error while getting host: %s", err), http.StatusInternalServerError) - return - } - defer rhost.Close() - log.Println("Rendering HostInfo", view.HostInfo(rhost).Render(context.Background(), w)) - }) - r.Get("/host/{hostname}/stats", func(w http.ResponseWriter, r *http.Request) { - rhost, err := host.ConnectHost(host.URI_QEMU_SSH_SYSTEM, chi.URLParam(r, "hostname")) - if err != nil { - http.Error(w, fmt.Sprintf("error while getting host: %s", err), http.StatusInternalServerError) - return - } - defer rhost.Close() - log.Println("Rendering stats", view.SysInfo(rhost).Render(context.Background(), w)) - }) - }) - - log.Println(http.ListenAndServe(":3000", r)) + r := server.New("127.0.0.1", 3000, clst) + r.Start() } diff --git a/router/errors.go b/router/errors.go new file mode 100644 index 0000000..6e5ac9a --- /dev/null +++ b/router/errors.go @@ -0,0 +1,6 @@ +package router + +import "errors" + +// Errors uses by the router module and it's sub modules +var ErrMethodNotExist = errors.New("http method doesn't exist") diff --git a/router/htmx/htmx.go b/router/htmx/htmx.go index dc0d520..3a480e7 100644 --- a/router/htmx/htmx.go +++ b/router/htmx/htmx.go @@ -1,4 +1,54 @@ -// Package htmx contains the routes for the WebUI HTMX +// Package htmx contains the routes for the WebUI HTMX package htmx +import ( + "errors" + "fmt" + "net/http" + "git.staur.ca/stobbsm/clustvirt/cluster" + "git.staur.ca/stobbsm/clustvirt/lib/log" + "git.staur.ca/stobbsm/clustvirt/router" + "github.com/go-chi/chi/v5" +) + +type htmx struct { + router chi.Router + routes []router.Route +} + +func (h *htmx) MountTo(c *cluster.Cluster, rr chi.Router) error { + var errs []error + for _, r := range h.routes { + switch r.Method { + case http.MethodTrace: + h.router.Trace(r.Path, r.Handler(c)) + case http.MethodOptions: + h.router.Options(r.Path, r.Handler(c)) + case http.MethodConnect: + h.router.Connect(r.Path, r.Handler(c)) + case http.MethodHead: + h.router.Head(r.Path, r.Handler(c)) + case http.MethodGet: + h.router.Get(r.Path, r.Handler(c)) + case http.MethodPost: + h.router.Post(r.Path, r.Handler(c)) + case http.MethodPut: + h.router.Put(r.Path, r.Handler(c)) + case http.MethodPatch: + h.router.Patch(r.Path, r.Handler(c)) + case http.MethodDelete: + h.router.Delete(r.Path, r.Handler(c)) + default: + err := fmt.Errorf("method: %s: %w", r.Method, router.ErrMethodNotExist) + errs = append(errs, err) + continue + } + log.Info("htmx.MoutnTo"). + Str("Route.Path", r.Path). + Str("Route.Method", r.Method). + Msg("route registered") + } + rr.Mount("/htmx", h.router) + return errors.Join(errs...) +} diff --git a/router/htmx/routes.go b/router/htmx/routes.go new file mode 100644 index 0000000..7e91c9c --- /dev/null +++ b/router/htmx/routes.go @@ -0,0 +1,68 @@ +package htmx + +import ( + "context" + "net/http" + + "git.staur.ca/stobbsm/clustvirt/cluster" + "git.staur.ca/stobbsm/clustvirt/lib/log" + "git.staur.ca/stobbsm/clustvirt/router" + "git.staur.ca/stobbsm/clustvirt/view" + "github.com/go-chi/chi/v5" +) + +var Htmx = &htmx{ + router: chi.NewRouter(), + routes: []router.Route{ + router.Route{ + Method: http.MethodGet, + Path: "/host/{hostname}", + Handler: func(c *cluster.Cluster) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + host, err := c.GetHost(chi.URLParam(r, "hostname")) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + log.Error("htmx.Handler"). + Str("uri", r.RequestURI). + Err(err). + Int("statusCode", http.StatusBadRequest). + Send() + } + if err = view.HostInfo(host).Render(context.Background(), w); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + log.Error("htmx.Handler"). + Str("uri", r.RequestURI). + Err(err). + Int("statusCode", http.StatusInternalServerError). + Send() + } + } + }, + }, + router.Route{ + Method: http.MethodGet, + Path: "/host/{hostname}/stats", + Handler: func(c *cluster.Cluster) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + host, err := c.GetHost(chi.URLParam(r, "hostname")) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + log.Error("htmx.Handler"). + Str("uri", r.RequestURI). + Err(err). + Int("statusCode", http.StatusBadRequest). + Send() + } + if err = view.HostStats(host).Render(context.Background(), w); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + log.Error("htmx.Handler"). + Str("uri", r.RequestURI). + Err(err). + Int("statusCode", http.StatusInternalServerError). + Send() + } + } + }, + }, + }, +} diff --git a/router/middleware/fromchi.go b/router/middleware/fromchi.go new file mode 100644 index 0000000..3297203 --- /dev/null +++ b/router/middleware/fromchi.go @@ -0,0 +1,22 @@ +package middleware + +import "github.com/go-chi/chi/v5/middleware" + +// These middlewares are cloned from chi/middleware +var ( + AllowContentEncoding = middleware.AllowContentEncoding + AllowContentType = middleware.AllowContentType + BasicAuth = middleware.BasicAuth + Compress = middleware.Compress + Heartbeat = middleware.Heartbeat + NoCache = middleware.NoCache + Profiler = middleware.Profiler + RealIP = middleware.RealIP + Recoverer = middleware.Recoverer + RequestID = middleware.RequestID + SetHeader = middleware.SetHeader + Sunset = middleware.Sunset + Throttle = middleware.Throttle + Timeout = middleware.Timeout + URLFormat = middleware.URLFormat +) diff --git a/router/router.go b/router/router.go index 943366c..1f81aa0 100644 --- a/router/router.go +++ b/router/router.go @@ -1,57 +1,28 @@ -// Package router defines the base routes http server package router import ( - "fmt" "net/http" - "os" - "strings" - "time" - "git.staur.ca/stobbsm/clustvirt/lib/log" + "git.staur.ca/stobbsm/clustvirt/cluster" "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" ) -type Server struct { - bindAddr string - ssl bool - middleware []http.Handler +// Types that are shared among routers + +// SubRouter defines an interface to be able to add a subrouter to a +// chi router +type SubRouter interface { + MountTo(*cluster.Cluster, chi.Router) error } -// New creates a new HTTP Server instance. -// Requires the IP and port number to bind to -func New(listen string, port int) *Server { - s := &Server{bindAddr: fmt.Sprintf("%s:%d", listen, port)} - - return s +// Route defines a route that should be added to a chi router or +// subrouter +type Route struct { + Method string + Path string + Handler RouteHandler } -// Start starts the server and initializes the router and common middleware -func (s *Server) Start() { - tstart := time.Now() - defer func() { - log.Info("router.Server.Start"). - Dur("upTime", time.Since(tstart)). - Msg("http server stopped") - }() - router := chi.NewRouter() - - indev, _ := os.LookupEnv("CLUSTVIRT_DEV") - indev = strings.ToLower(indev) - switch indev { - case "true": - fallthrough - case "1": - fallthrough - case "yes": - fallthrough - case "on": - router.Use(middleware.NoCache) - } -} - -func (s *Server) AddMiddleware(m http.Handler) { - s.middleware = append(s.middleware, - m) -} +// RouteHandler gets the active cluster for the server to inject into +// the http.HandlerFunc that is then returned +type RouteHandler func(*cluster.Cluster) http.HandlerFunc diff --git a/router/server/root.go b/router/server/root.go new file mode 100644 index 0000000..62d2894 --- /dev/null +++ b/router/server/root.go @@ -0,0 +1,21 @@ +package server + +import ( + "net/http" + + "git.staur.ca/stobbsm/clustvirt/view" + "git.staur.ca/stobbsm/clustvirt/view/static" + "github.com/a-h/templ" +) + +// Root based routes for ClustVirt + +func (s *Server) baseRoutes() { + // fileserver for static content + fs := http.StripPrefix("/static", http.FileServer(http.Dir("public"))) + s.r.Get("/static/*", fs.ServeHTTP) + + // Root defaults to the cluster view + s.r.Get("/", templ.Handler(view.ClusterInfo(s.c)).ServeHTTP) + s.r.Get("/about", templ.Handler(static.About()).ServeHTTP) +} diff --git a/router/server/server.go b/router/server/server.go new file mode 100644 index 0000000..8ac34c8 --- /dev/null +++ b/router/server/server.go @@ -0,0 +1,80 @@ +package server + +import ( + "fmt" + "net/http" + "os" + "strings" + "time" + + "git.staur.ca/stobbsm/clustvirt/cluster" + "git.staur.ca/stobbsm/clustvirt/lib/log" + "git.staur.ca/stobbsm/clustvirt/router" + "git.staur.ca/stobbsm/clustvirt/router/htmx" + "git.staur.ca/stobbsm/clustvirt/router/middleware" + "github.com/go-chi/chi/v5" +) + +// Server represents an HTTP server that uses chi for routes +type Server struct { + bindAddr string + ssl bool + c *cluster.Cluster + r chi.Router +} + +// New creates a new HTTP Server instance. +// Requires the IP and port number to bind to +func New(listen string, port int, cluster *cluster.Cluster) *Server { + s := &Server{bindAddr: fmt.Sprintf("%s:%d", listen, port), c: cluster} + + return s +} + +// Start starts the server and initializes the router and common middleware +func (s *Server) Start() { + tstart := time.Now() + defer func() { + log.Info("router.Server.Start"). + Dur("upTime", time.Since(tstart)). + Msg("http server stopped") + }() + s.r = chi.NewRouter() + + indev, _ := os.LookupEnv("CLUSTVIRT_DEV") + indev = strings.ToLower(indev) + switch indev { + case "true": + fallthrough + case "1": + fallthrough + case "yes": + fallthrough + case "on": + s.AddMiddleware(middleware.NoCache) + s.r.Get("/_/debug", middleware.Profiler().ServeHTTP) + } + // Add routes + s.baseRoutes() + if err := s.AddSubRouter(htmx.Htmx); err != nil { + log.Error("server.Start"). + Str("subroute", "htmx"). + Err(err).Send() + } + // Start the server + if err := http.ListenAndServe(s.bindAddr, s.r); err != nil { + log.Error("router.Server.Start"). + Str("bindaddr", s.bindAddr). + Err(err).Send() + } +} + +// AddMiddleware appends middleware to the chi router +func (s *Server) AddMiddleware(m ...func(h http.Handler) http.Handler) { + s.r.Use(m...) +} + +// AddSubRouter attachs a SubRouter using it's MountTo method +func (s *Server) AddSubRouter(sr router.SubRouter) error { + return sr.MountTo(s.c, s.r) +} diff --git a/view/cluster.templ b/view/cluster.templ index f2cd565..6aef8eb 100644 --- a/view/cluster.templ +++ b/view/cluster.templ @@ -7,14 +7,18 @@ import ( "git.staur.ca/stobbsm/clustvirt/view/layouts" ) -templ ClusterInfo(cs *cluster.ClusterStats, diff cluster.StatDiff, navbar []components.NavItem) { - @layouts.Manager("ClustVirt", "Cluster Manager", navbar) { +templ ClusterInfo(c *cluster.Cluster) { + @layouts.Manager("ClustVirt", "Cluster Manager", components.DefaultNavBar) {

Cluster Stats

- @CPUStats(cs, diff) + @ClusterStats(cluster.InitStats(c)) } } -templ CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) { +templ ClusterStats(cs *cluster.ClusterStats) { + @CPUStats(cs.CPU, cs.Diff().CPU) +} + +templ CPUStats(stats cluster.CPUStats, diff cluster.CPUDiff) { + - - - - - + + + + + + - - - - - + + + + + +
CPU stats @@ -26,42 +30,25 @@ templ CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) {
Cores Threads AllocatedMHz
- Latest - - { fmt.Sprint(cs.CPU.Sockets) } - - { fmt.Sprint(cs.CPU.Cores) } - - { fmt.Sprint(cs.CPU.Threads) } - - { fmt.Sprint(cs.CPU.Allocated) } - Latest{ fmt.Sprint(stats.Sockets) }{ fmt.Sprint(stats.Cores) }{ fmt.Sprint(stats.Threads) }{ fmt.Sprint(stats.Allocated) }{ fmt.Sprint(stats.MHz) }
- Change - - { fmt.Sprint(diff.CPU.Sockets) } - - { fmt.Sprint(diff.CPU.Cores) } - - { fmt.Sprint(diff.CPU.Threads) } - - { fmt.Sprint(diff.CPU.Allocated) } - Change{ fmt.Sprint(diff.Sockets) }{ fmt.Sprint(diff.Cores) }{ fmt.Sprint(diff.Threads) }{ fmt.Sprint(diff.Allocated) }{ fmt.Sprint(diff.MHz) }
diff --git a/view/cluster_templ.go b/view/cluster_templ.go index 80bdae9..475d493 100644 --- a/view/cluster_templ.go +++ b/view/cluster_templ.go @@ -17,7 +17,7 @@ import ( "git.staur.ca/stobbsm/clustvirt/view/layouts" ) -func ClusterInfo(cs *cluster.ClusterStats, diff cluster.StatDiff, navbar []components.NavItem) templ.Component { +func ClusterInfo(c *cluster.Cluster) templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) if !templ_7745c5c3_IsBuffer { @@ -40,7 +40,7 @@ func ClusterInfo(cs *cluster.ClusterStats, diff cluster.StatDiff, navbar []compo if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = CPUStats(cs, diff).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = ClusterStats(cluster.InitStats(c)).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -49,7 +49,7 @@ func ClusterInfo(cs *cluster.ClusterStats, diff cluster.StatDiff, navbar []compo } return templ_7745c5c3_Err }) - templ_7745c5c3_Err = layouts.Manager("ClustVirt", "Cluster Manager", navbar).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layouts.Manager("ClustVirt", "Cluster Manager", components.DefaultNavBar).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -60,7 +60,7 @@ func ClusterInfo(cs *cluster.ClusterStats, diff cluster.StatDiff, navbar []compo }) } -func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { +func ClusterStats(cs *cluster.ClusterStats) templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) if !templ_7745c5c3_IsBuffer { @@ -73,8 +73,32 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { templ_7745c5c3_Var3 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - var templ_7745c5c3_Var4 = []any{"table-auto", "w-full"} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...) + templ_7745c5c3_Err = CPUStats(cs.CPU, cs.Diff().CPU).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if !templ_7745c5c3_IsBuffer { + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) + } + return templ_7745c5c3_Err + }) +} + +func CPUStats(stats cluster.CPUStats, diff cluster.CPUDiff) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) + if !templ_7745c5c3_IsBuffer { + templ_7745c5c3_Buffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var4 := templ.GetChildren(ctx) + if templ_7745c5c3_Var4 == nil { + templ_7745c5c3_Var4 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + var templ_7745c5c3_Var5 = []any{"table-auto", "w-full"} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -82,7 +106,7 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var4).String())) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var5).String())) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -90,8 +114,8 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var5 = []any{"caption-top"} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...) + var templ_7745c5c3_Var6 = []any{"caption-top"} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -99,31 +123,18 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var5).String())) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var6).String())) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">CPU stats SocketsCoresThreadsAllocated Latest") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var6 string - templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(cs.CPU.Sockets)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 36, Col: 33} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">CPU stats SocketsCoresThreadsAllocatedMHz Latest") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var7 string - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(cs.CPU.Cores)) + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(stats.Sockets)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 39, Col: 31} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 38, Col: 35} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -134,9 +145,9 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var8 string - templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(cs.CPU.Threads)) + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(stats.Cores)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 42, Col: 33} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 39, Col: 33} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -147,22 +158,22 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var9 string - templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(cs.CPU.Allocated)) + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(stats.Threads)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 45, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 40, Col: 35} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Change") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.CPU.Sockets)) + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(stats.Allocated)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 53, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 41, Col: 37} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) if templ_7745c5c3_Err != nil { @@ -173,22 +184,22 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var11 string - templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.CPU.Cores)) + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(stats.MHz)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 56, Col: 33} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 42, Col: 31} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Change") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.CPU.Threads)) + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.Sockets)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 59, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 46, Col: 34} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { @@ -199,14 +210,53 @@ func CPUStats(cs *cluster.ClusterStats, diff cluster.StatDiff) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.CPU.Allocated)) + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.Cores)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 62, Col: 37} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 47, Col: 32} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.Threads)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 48, Col: 34} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var15 string + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.Allocated)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 49, Col: 36} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var16 string + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(diff.MHz)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/cluster.templ`, Line: 50, Col: 30} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err diff --git a/view/components/defaults.go b/view/components/defaults.go new file mode 100644 index 0000000..23d9039 --- /dev/null +++ b/view/components/defaults.go @@ -0,0 +1,7 @@ +package components + +var DefaultNavBar = []NavItem{ + {Name: "Cluster", Href: "/"}, + {Name: "Configuration", Href: "/config"}, + {Name: "About", Href: "/about"}, +} diff --git a/view/hostinfo.templ b/view/hostinfo.templ index 9d9e052..9ddce6d 100644 --- a/view/hostinfo.templ +++ b/view/hostinfo.templ @@ -123,11 +123,11 @@ templ HostInfo(h *host.Host) {
- @SysInfo(h) + @HostStats(h)
} -templ SysInfo(h *host.Host) { +templ HostStats(h *host.Host) { @MemChart(h) } diff --git a/view/hostinfo_templ.go b/view/hostinfo_templ.go index 3bb5d56..fa26d8d 100644 --- a/view/hostinfo_templ.go +++ b/view/hostinfo_templ.go @@ -505,7 +505,7 @@ func HostInfo(h *host.Host) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = SysInfo(h).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = HostStats(h).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -520,7 +520,7 @@ func HostInfo(h *host.Host) templ.Component { }) } -func SysInfo(h *host.Host) templ.Component { +func HostStats(h *host.Host) templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) if !templ_7745c5c3_IsBuffer { diff --git a/view/static/home.templ b/view/static/home.templ index 5575c48..ee4faeb 100644 --- a/view/static/home.templ +++ b/view/static/home.templ @@ -5,7 +5,7 @@ import ( "git.staur.ca/stobbsm/clustvirt/view/components" ) -templ Home() { +templ About() { @layouts.StaticPage("ClustVirt", "Libvirt, clustered and managed", []components.NavItem{ {Name: "What", Href: "#"}, {Name: "Why", Href: "#"}, diff --git a/view/static/home_templ.go b/view/static/home_templ.go index 13c3c03..6cb46fc 100644 --- a/view/static/home_templ.go +++ b/view/static/home_templ.go @@ -15,7 +15,7 @@ import ( "git.staur.ca/stobbsm/clustvirt/view/layouts" ) -func Home() templ.Component { +func About() templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) if !templ_7745c5c3_IsBuffer {