continued work on the GUI

This commit is contained in:
Jeff Carr 2025-03-24 21:53:31 -05:00
parent a7e639cdb0
commit 1fd6b1d36d
6 changed files with 126 additions and 63 deletions

26
argv.go
View File

@ -16,36 +16,16 @@ type args struct {
Port int `arg:"--port" default:"8080" help:"allow droplet events via http"`
Server string `arg:"env:VIRTIGO_SERVER" help:"what virtigo cluster to connect to"`
Xml []string `arg:"--libvirt" help:"import qemu xml files: --libvirt /etc/libvirt/qemu/*.xml"`
Admin bool `arg:"--admin" help:"enter admin mode"`
}
// Daemon bool `arg:"--daemon" help:"run in daemon mode"`
// IgnoreCpu bool `arg:"--xml-ignore-cpu" default:"true" help:"ignore non-standard libvirt xml cpus"`
// IgnoreBr bool `arg:"--xml-ignore-net" default:"true" help:"ignore network bridge name changes"`
// IgnDisk bool `arg:"--xml-ignore-disk" default:"false" help:"ignore duplicate disk names"`
// Save bool `arg:"--save" default:"false" help:"save protobuf config after import"`
// Start string `arg:"--start" help:"start a droplet"`
// Uptime bool `arg:"--uptime" default:"true" help:"allow uptime checks for things like Kuma"`
// Hosts []string `arg:"--hosts" help:"hosts to connect to"`
func (a args) Description() string {
return `
virtigo will help control your cluster
virtigo: control your cluster
This maintains a master list of all your vm's (aka 'droplets')
in your homelab cloud. You can import libvirt xml files.
This app talks to your hypervisors via the virtigod daemon.
At this time, this _only_ supports qcow2 images. If you need
something else you'll have to add it in virtigolib.
This runs a http server so you can control your virtual machines.
For example to start a vm called 'www.wit.com' your cluster 'foo.bar.com':
curl http://virtigo.foo.com/start?www.wit.com
Use 'virtigoctl' to import xml files from libvirt and configure new
hypervisors in your cluster.
`
}
@ -56,7 +36,6 @@ func (args) Version() string {
var INFO *log.LogFlag
var POLL *log.LogFlag
var WARN *log.LogFlag
var SPEW *log.LogFlag
var EVENT *log.LogFlag
func init() {
@ -66,6 +45,5 @@ func init() {
INFO = log.NewFlag("INFO", false, full, short, "general virtigo")
POLL = log.NewFlag("POLL", false, full, short, "virtigo polling")
WARN = log.NewFlag("WARN", true, full, short, "bad things")
SPEW = log.NewFlag("SPEW", true, full, short, "dump everything")
EVENT = log.NewFlag("EVENT", true, full, short, "hypeprvisor/droplet events")
}

View File

@ -10,6 +10,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"os/user"
"time"
@ -26,11 +27,15 @@ func (admin *adminT) refresh() {
log.Info("virtigo scan here")
}
url := argv.Server
if admin.url == nil {
log.Info("admin url == nil")
return
}
msg := []byte(`{"message": "Hello"}`)
// display the uptime
if data, err := postData(url+"/uptime", msg); err != nil {
if data, err := postData(admin.url.String()+"/uptime", msg); err != nil {
log.Info("/uptime Error:", err)
} else {
log.Info("Response:", string(data))
@ -38,7 +43,7 @@ func (admin *adminT) refresh() {
}
// update the droplet list
if data, err := postData(url+"/DropletsPB", msg); err != nil {
if data, err := postData(admin.url.String()+"/DropletsPB", msg); err != nil {
log.Info("/DropletsPB Error:", err)
} else {
fmt.Println("DropletsPB Response len:", len(data))
@ -51,7 +56,7 @@ func (admin *adminT) refresh() {
}
// update the hypervisor list
if data, err := postData(url+"/HypervisorsPB", msg); err != nil {
if data, err := postData(admin.url.String()+"/HypervisorsPB", msg); err != nil {
log.Info("Error:", err)
} else {
fmt.Println("HypervisorsPB Response len:", len(data))
@ -64,7 +69,7 @@ func (admin *adminT) refresh() {
}
// update the events list
if data, err := postData(url+"/EventsPB", msg); err != nil {
if data, err := postData(admin.url.String()+"/EventsPB", msg); err != nil {
log.Info("Error:", err)
} else {
fmt.Println("EventsPB Response len:", len(data))
@ -92,11 +97,12 @@ func (admin *adminT) doAdminGui() {
Timeout: 10 * time.Second, // Set a reasonable timeout
}
win := gadgets.NewGenericWindow("Virtigo: (run your cluster)", "virtigo stuff")
win := gadgets.NewGenericWindow("Virtigo: (run your cluster)", "localhost")
win.Custom = func() {
log.Warn("Main window close")
os.Exit(0)
}
me.gwin = win
admin.uptime = win.Group.NewLabel("uptime")
@ -161,6 +167,14 @@ func (admin *adminT) doAdminGui() {
// okExit("admin close")
})
me.cmap = make(map[*virtpb.Cluster]*adminT)
for c := range me.clusters.IterAll() {
a := new(adminT)
me.cmap[c] = a
log.Info("found in the config file", c.URL)
a.makeClusterGroup(c)
}
// sit here forever refreshing the GUI
for {
admin.refresh()
@ -168,6 +182,67 @@ func (admin *adminT) doAdminGui() {
}
}
func (admin *adminT) makeClusterGroup(c *virtpb.Cluster) {
var err error
admin.url, err = url.Parse(c.URL)
if err != nil {
badExit(err)
}
group := me.gwin.Bottom.NewGroup(admin.url.Hostname())
admin.uptime = group.NewLabel("uptime")
grid := group.RawGrid()
grid.NewButton("show hypervisors", func() {
if admin.hypervisors == nil {
log.Info("hypervisors not initialized")
return
}
log.Info("Hypervisors len=", admin.hypervisors.Len())
admin.hwin = newHypervisorsWindow()
admin.hwin.doStdHypervisors(admin.hypervisors)
admin.hwin.win.Custom = func() {
log.Info("hiding table window")
}
})
grid.NewButton("droplets", func() {
if admin.droplets == nil {
log.Info("droplets not initialized")
return
}
admin.dwin = newDropletsWindow()
admin.dwin.win.Custom = func() {
log.Info("hiding droplet table window")
}
var found *virtpb.Droplets
found = virtpb.NewDroplets()
all := admin.droplets.All()
for all.Scan() {
vm := all.Next()
if vm.Current.State != virtpb.DropletState_ON {
continue
}
found.Append(vm)
}
admin.dwin.doActiveDroplets(found)
})
grid.NewButton("events", func() {
if admin.events == nil {
log.Info("events are not initialized")
return
}
log.Info("Events len=", admin.events.Len())
admin.ewin = newEventsWindow()
admin.ewin.doStdEvents(admin.events)
admin.ewin.win.Custom = func() {
log.Info("hiding table window")
}
})
}
func postData(url string, data []byte) ([]byte, error) {
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data))
if err != nil {
@ -191,22 +266,3 @@ func postData(url string, data []byte) ([]byte, error) {
return body, nil
}
/*
func main() {
url := "http://example.com/endpoint"
data := []byte(`{"message": "Hello"}`)
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for range ticker.C {
response, err := postData(url, data)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Response:", string(response))
}
}
}
*/

View File

@ -45,17 +45,6 @@ func doGui() {
func drawWindow(win *gadgets.GenericWindow) {
grid := win.Group.RawGrid()
/*
var hyperWin *gadgets.GenericWindow
grid.NewButton("hypervisors", func() {
if hyperWin != nil {
hyperWin.Toggle()
return
}
hyperWin = makeHypervisorsWindow(me.cluster.H)
})
*/
var newHyperWin *stdHypervisorTableWin
grid.NewButton("show hypervisors", func() {
if newHyperWin != nil {

36
main.go
View File

@ -5,10 +5,12 @@ package main
import (
"embed"
"fmt"
"net/url"
"os"
"path/filepath"
"time"
"github.com/google/uuid"
"go.wit.com/dev/alexflint/arg"
"go.wit.com/gui"
"go.wit.com/lib/protobuf/virtpb"
@ -37,12 +39,44 @@ func main() {
os.Setenv("VIRTIGO_HOME", fullpath)
}
if argv.Server != "" {
me.clusters = virtpb.NewClusters()
if argv.Admin {
err := me.clusters.ConfigLoad()
if err != nil {
badExit(err)
}
me.admin = new(adminT)
me.admin.doAdminGui()
okExit("admin close")
}
if argv.Server != "" {
log.Info("start admin interface")
me.admin = new(adminT)
var err error
me.admin.url, err = url.Parse(argv.Server)
if err != nil {
badExit(err)
}
err = me.clusters.ConfigLoad()
if err != nil {
clusters := virtpb.NewClusters()
c := new(virtpb.Cluster)
c.Uuid = uuid.New().String()
c.URL = argv.Server
clusters.Append(c)
virtpb.ConfigWriteTEXT(clusters, "cluster.text")
badExit(err)
}
me.admin.doAdminGui()
okExit("admin close")
}
os.Exit(-1)
// set defaults
me.unstable = time.Now() // initialize the grid as unstable
me.changed = false

View File

@ -1,9 +1,11 @@
package main
import (
"net/url"
"time"
"go.wit.com/gui"
"go.wit.com/lib/gadgets"
"go.wit.com/lib/protobuf/virtpb"
)
@ -21,7 +23,7 @@ func (b *virtigoT) Enable() {
// this app's variables
type virtigoT struct {
cluster *virtpb.Cluster // basic cluster settings
cluster *virtpb.OldCluster // basic cluster settings
myGui *gui.Node // the gui toolkit handle
e *virtpb.Events // virt protobuf events
hmap map[*virtpb.Hypervisor]*HyperT // map to the local struct
@ -37,6 +39,9 @@ type virtigoT struct {
status *gui.Node // the cluster status
lastuptime *gui.Node // the last time uptime was checked by Kuma
admin *adminT // the admin struct
clusters *virtpb.Clusters // clusters protobuf
cmap map[*virtpb.Cluster]*adminT // map to local GUI objects and the protobuf
gwin *gadgets.GenericWindow // main window
}
type adminT struct {
@ -48,6 +53,7 @@ type adminT struct {
dwin *stdDropletTableWin // the droplet window
hwin *stdHypervisorTableWin // the hypervisor window
ewin *stdEventTableWin // the events window
url *url.URL // URL for the cloud
}
// the stuff that is needed for a hypervisor

View File

@ -161,7 +161,7 @@ func (dw *stdDropletTableWin) doInactiveDroplets(pb *virtpb.Droplets) {
return d.Current.Hypervisor
})
*/
vp := t.AddButtonFunc("Configure Hostname", func(p *virtpb.Droplet) string {
vp := t.AddButtonFunc("Verify Config", func(p *virtpb.Droplet) string {
return p.Hostname
})
vp.Custom = func(d *virtpb.Droplet) {