continued work on the GUI
This commit is contained in:
parent
a7e639cdb0
commit
1fd6b1d36d
26
argv.go
26
argv.go
|
@ -16,36 +16,16 @@ type args struct {
|
||||||
Port int `arg:"--port" default:"8080" help:"allow droplet events via http"`
|
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"`
|
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"`
|
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 {
|
func (a args) Description() string {
|
||||||
return `
|
return `
|
||||||
virtigo will help control your cluster
|
virtigo: control your cluster
|
||||||
|
|
||||||
This maintains a master list of all your vm's (aka 'droplets')
|
This maintains a master list of all your vm's (aka 'droplets')
|
||||||
in your homelab cloud. You can import libvirt xml files.
|
in your homelab cloud. You can import libvirt xml files.
|
||||||
This app talks to your hypervisors via the virtigod daemon.
|
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 INFO *log.LogFlag
|
||||||
var POLL *log.LogFlag
|
var POLL *log.LogFlag
|
||||||
var WARN *log.LogFlag
|
var WARN *log.LogFlag
|
||||||
var SPEW *log.LogFlag
|
|
||||||
var EVENT *log.LogFlag
|
var EVENT *log.LogFlag
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -66,6 +45,5 @@ func init() {
|
||||||
INFO = log.NewFlag("INFO", false, full, short, "general virtigo")
|
INFO = log.NewFlag("INFO", false, full, short, "general virtigo")
|
||||||
POLL = log.NewFlag("POLL", false, full, short, "virtigo polling")
|
POLL = log.NewFlag("POLL", false, full, short, "virtigo polling")
|
||||||
WARN = log.NewFlag("WARN", true, full, short, "bad things")
|
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")
|
EVENT = log.NewFlag("EVENT", true, full, short, "hypeprvisor/droplet events")
|
||||||
}
|
}
|
||||||
|
|
106
doAdminGui.go
106
doAdminGui.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"time"
|
"time"
|
||||||
|
@ -26,11 +27,15 @@ func (admin *adminT) refresh() {
|
||||||
log.Info("virtigo scan here")
|
log.Info("virtigo scan here")
|
||||||
}
|
}
|
||||||
|
|
||||||
url := argv.Server
|
if admin.url == nil {
|
||||||
|
log.Info("admin url == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
msg := []byte(`{"message": "Hello"}`)
|
msg := []byte(`{"message": "Hello"}`)
|
||||||
|
|
||||||
// display the uptime
|
// 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)
|
log.Info("/uptime Error:", err)
|
||||||
} else {
|
} else {
|
||||||
log.Info("Response:", string(data))
|
log.Info("Response:", string(data))
|
||||||
|
@ -38,7 +43,7 @@ func (admin *adminT) refresh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the droplet list
|
// 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)
|
log.Info("/DropletsPB Error:", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("DropletsPB Response len:", len(data))
|
fmt.Println("DropletsPB Response len:", len(data))
|
||||||
|
@ -51,7 +56,7 @@ func (admin *adminT) refresh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the hypervisor list
|
// 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)
|
log.Info("Error:", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("HypervisorsPB Response len:", len(data))
|
fmt.Println("HypervisorsPB Response len:", len(data))
|
||||||
|
@ -64,7 +69,7 @@ func (admin *adminT) refresh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the events list
|
// 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)
|
log.Info("Error:", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("EventsPB Response len:", len(data))
|
fmt.Println("EventsPB Response len:", len(data))
|
||||||
|
@ -92,11 +97,12 @@ func (admin *adminT) doAdminGui() {
|
||||||
Timeout: 10 * time.Second, // Set a reasonable timeout
|
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() {
|
win.Custom = func() {
|
||||||
log.Warn("Main window close")
|
log.Warn("Main window close")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
me.gwin = win
|
||||||
|
|
||||||
admin.uptime = win.Group.NewLabel("uptime")
|
admin.uptime = win.Group.NewLabel("uptime")
|
||||||
|
|
||||||
|
@ -161,6 +167,14 @@ func (admin *adminT) doAdminGui() {
|
||||||
// okExit("admin close")
|
// 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
|
// sit here forever refreshing the GUI
|
||||||
for {
|
for {
|
||||||
admin.refresh()
|
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) {
|
func postData(url string, data []byte) ([]byte, error) {
|
||||||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data))
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -191,22 +266,3 @@ func postData(url string, data []byte) ([]byte, error) {
|
||||||
|
|
||||||
return body, nil
|
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
11
doGui.go
11
doGui.go
|
@ -45,17 +45,6 @@ func doGui() {
|
||||||
func drawWindow(win *gadgets.GenericWindow) {
|
func drawWindow(win *gadgets.GenericWindow) {
|
||||||
grid := win.Group.RawGrid()
|
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
|
var newHyperWin *stdHypervisorTableWin
|
||||||
grid.NewButton("show hypervisors", func() {
|
grid.NewButton("show hypervisors", func() {
|
||||||
if newHyperWin != nil {
|
if newHyperWin != nil {
|
||||||
|
|
36
main.go
36
main.go
|
@ -5,10 +5,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"go.wit.com/dev/alexflint/arg"
|
"go.wit.com/dev/alexflint/arg"
|
||||||
"go.wit.com/gui"
|
"go.wit.com/gui"
|
||||||
"go.wit.com/lib/protobuf/virtpb"
|
"go.wit.com/lib/protobuf/virtpb"
|
||||||
|
@ -37,12 +39,44 @@ func main() {
|
||||||
os.Setenv("VIRTIGO_HOME", fullpath)
|
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 = new(adminT)
|
||||||
me.admin.doAdminGui()
|
me.admin.doAdminGui()
|
||||||
okExit("admin close")
|
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
|
// set defaults
|
||||||
me.unstable = time.Now() // initialize the grid as unstable
|
me.unstable = time.Now() // initialize the grid as unstable
|
||||||
me.changed = false
|
me.changed = false
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.wit.com/gui"
|
"go.wit.com/gui"
|
||||||
|
"go.wit.com/lib/gadgets"
|
||||||
"go.wit.com/lib/protobuf/virtpb"
|
"go.wit.com/lib/protobuf/virtpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ func (b *virtigoT) Enable() {
|
||||||
|
|
||||||
// this app's variables
|
// this app's variables
|
||||||
type virtigoT struct {
|
type virtigoT struct {
|
||||||
cluster *virtpb.Cluster // basic cluster settings
|
cluster *virtpb.OldCluster // basic cluster settings
|
||||||
myGui *gui.Node // the gui toolkit handle
|
myGui *gui.Node // the gui toolkit handle
|
||||||
e *virtpb.Events // virt protobuf events
|
e *virtpb.Events // virt protobuf events
|
||||||
hmap map[*virtpb.Hypervisor]*HyperT // map to the local struct
|
hmap map[*virtpb.Hypervisor]*HyperT // map to the local struct
|
||||||
|
@ -37,6 +39,9 @@ type virtigoT struct {
|
||||||
status *gui.Node // the cluster status
|
status *gui.Node // the cluster status
|
||||||
lastuptime *gui.Node // the last time uptime was checked by Kuma
|
lastuptime *gui.Node // the last time uptime was checked by Kuma
|
||||||
admin *adminT // the admin struct
|
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 {
|
type adminT struct {
|
||||||
|
@ -48,6 +53,7 @@ type adminT struct {
|
||||||
dwin *stdDropletTableWin // the droplet window
|
dwin *stdDropletTableWin // the droplet window
|
||||||
hwin *stdHypervisorTableWin // the hypervisor window
|
hwin *stdHypervisorTableWin // the hypervisor window
|
||||||
ewin *stdEventTableWin // the events window
|
ewin *stdEventTableWin // the events window
|
||||||
|
url *url.URL // URL for the cloud
|
||||||
}
|
}
|
||||||
|
|
||||||
// the stuff that is needed for a hypervisor
|
// the stuff that is needed for a hypervisor
|
||||||
|
|
|
@ -161,7 +161,7 @@ func (dw *stdDropletTableWin) doInactiveDroplets(pb *virtpb.Droplets) {
|
||||||
return d.Current.Hypervisor
|
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
|
return p.Hostname
|
||||||
})
|
})
|
||||||
vp.Custom = func(d *virtpb.Droplet) {
|
vp.Custom = func(d *virtpb.Droplet) {
|
||||||
|
|
Loading…
Reference in New Issue