Compare commits
No commits in common. "master" and "v0.2.6" have entirely different histories.
|
@ -1,3 +1,5 @@
|
||||||
go.*
|
go.*
|
||||||
|
|
||||||
*.pb.go
|
*.pb.go
|
||||||
*.swp
|
|
||||||
|
example/example
|
||||||
|
|
52
Makefile
52
Makefile
|
@ -3,15 +3,33 @@
|
||||||
# cd ~/go/src/google.golang.org/protobuf/cmd/protoc-gen-go
|
# cd ~/go/src/google.golang.org/protobuf/cmd/protoc-gen-go
|
||||||
# go install
|
# go install
|
||||||
|
|
||||||
all: proto goimports vet
|
check-for-protoc-gen-go:
|
||||||
|
@if [ -f "/usr/bin/bin/protoc-gen-go" ]; then \
|
||||||
|
echo "the protoc-gen-go package is old in debian sid right now"; \
|
||||||
|
echo "for now, remove it"; \
|
||||||
|
apt remote proto-gen-go \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@if [ ! -f "$(HOME)/go/bin/protoc-gen-go" ]; then \
|
||||||
|
echo "you must build protoc-gen-go from google"; \
|
||||||
|
echo go-clone google.golang.org/protobuf; \
|
||||||
|
cd ~/go/src/google.golang.org/protobuf/cmd/protoc-gen-go/ && go install; \
|
||||||
|
fi
|
||||||
|
make all
|
||||||
|
|
||||||
vet:
|
all: droplet.pb.go hypervisor.pb.go event.pb.go experiments.pb.go
|
||||||
@GO111MODULE=off go vet
|
make -C example
|
||||||
@echo this go library package builds okay
|
|
||||||
|
vet: lint
|
||||||
|
GO111MODULE=off go vet
|
||||||
|
|
||||||
|
lint:
|
||||||
|
-buf lint droplet.proto
|
||||||
|
|
||||||
# autofixes your import headers in your golang files
|
# autofixes your import headers in your golang files
|
||||||
goimports:
|
goimports:
|
||||||
goimports -w *.go
|
goimports -w *.go
|
||||||
|
make -C example goimports
|
||||||
|
|
||||||
redomod:
|
redomod:
|
||||||
rm -f go.*
|
rm -f go.*
|
||||||
|
@ -21,20 +39,32 @@ redomod:
|
||||||
clean:
|
clean:
|
||||||
rm -f *.pb.go
|
rm -f *.pb.go
|
||||||
-rm -f go.*
|
-rm -f go.*
|
||||||
|
make -C example clean
|
||||||
proto:droplet.pb.go hypervisor.pb.go event.pb.go cluster.pb.go
|
|
||||||
|
|
||||||
droplet.pb.go: droplet.proto
|
droplet.pb.go: droplet.proto
|
||||||
autogenpb --proto droplet.proto
|
# protoc --go_out=. droplet.proto
|
||||||
|
# This is switched over to use the new protoc-gen-go from google.golang.org/protobuf/cmd/protoc-gen-go
|
||||||
|
# the debian one (2024/10/21) seems to be the older/original one from github.com/golang/protobuf/protoc-gen-go
|
||||||
|
cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
--go_opt=Mdroplet.proto=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
droplet.proto
|
||||||
|
|
||||||
hypervisor.pb.go: hypervisor.proto
|
hypervisor.pb.go: hypervisor.proto
|
||||||
autogenpb --proto hypervisor.proto
|
cd ~/go/src && protoc --go_out=. --proto_path=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
--go_opt=Mhypervisor.proto=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
hypervisor.proto
|
||||||
|
|
||||||
event.pb.go: event.proto
|
event.pb.go: event.proto
|
||||||
autogenpb --proto event.proto
|
cd ~/go/src && protoc --go_out=. \
|
||||||
|
--proto_path=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
--go_opt=Mevent.proto=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
event.proto
|
||||||
|
|
||||||
cluster.pb.go: cluster.proto
|
experiments.pb.go: experiments.proto
|
||||||
autogenpb --proto cluster.proto
|
cd ~/go/src && protoc --go_out=. \
|
||||||
|
--proto_path=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
--go_opt=Mexperiments.proto=go.wit.com/lib/protobuf/virtbuf \
|
||||||
|
experiments.proto
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
apt install golang-goprotobuf-dev
|
apt install golang-goprotobuf-dev
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
This go library handles the protobuf files
|
these are .proto files. first stab. may be inaccurate
|
||||||
and various functions for virtigo.
|
writes out config files as protojson and prototext
|
||||||
|
todo: lots of stuff
|
||||||
You must build the protobuf files using autogenpb
|
|
||||||
|
|
||||||
go install go.wit.com/apps/autogenpb@latest
|
|
||||||
|
|
60
add.go
60
add.go
|
@ -1,6 +1,7 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -8,8 +9,7 @@ import (
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
func (c *Cluster) InitDroplet(hostname string) (*Droplet, error) {
|
||||||
func (c *OldCluster) InitDroplet(hostname string) (*Droplet, error) {
|
|
||||||
var d *Droplet
|
var d *Droplet
|
||||||
d = new(Droplet)
|
d = new(Droplet)
|
||||||
d.Current = new(Current)
|
d.Current = new(Current)
|
||||||
|
@ -34,7 +34,6 @@ func (c *Cluster) appendDroplet(d *Droplet) {
|
||||||
|
|
||||||
c.d.Droplets = append(c.d.Droplets, d)
|
c.d.Droplets = append(c.d.Droplets, d)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// can the json protobuf output use a string and have a type handler
|
// can the json protobuf output use a string and have a type handler
|
||||||
// to convert it back to int64?
|
// to convert it back to int64?
|
||||||
|
@ -50,10 +49,10 @@ func (x *Hypervisor) SetMemoryGB(gb int) {
|
||||||
x.Memory = int64(gb * 1024 * 1024 * 1024)
|
x.Memory = int64(gb * 1024 * 1024 * 1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) FindDropletByName(name string) *Droplet {
|
func (c *Cluster) FindDropletByName(name string) *Droplet {
|
||||||
loop := c.d.All() // get the list of droplets
|
loop := c.DropletsAll() // get the list of droplets
|
||||||
for loop.Scan() {
|
for loop.Scan() {
|
||||||
d := loop.Next()
|
d := loop.Droplet()
|
||||||
if d.Hostname == name {
|
if d.Hostname == name {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
@ -61,20 +60,7 @@ func (c *OldCluster) FindDropletByName(name string) *Droplet {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) FindDropletByUuid(id string) *Droplet {
|
func (c *Cluster) FindHypervisorByName(name string) *Hypervisor {
|
||||||
/*
|
|
||||||
log.Info("START FIND", id)
|
|
||||||
loop := c.d.All() // get the list of droplets
|
|
||||||
for loop.Scan() {
|
|
||||||
d := loop.Next()
|
|
||||||
log.Info("droplet:", d.Hostname, d.Uuid)
|
|
||||||
}
|
|
||||||
log.Info("END FIND", id)
|
|
||||||
*/
|
|
||||||
return c.d.FindByUuid(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OldCluster) FindHypervisorByName(name string) *Hypervisor {
|
|
||||||
for _, h := range c.H.Hypervisors {
|
for _, h := range c.H.Hypervisors {
|
||||||
if h.Hostname == name {
|
if h.Hostname == name {
|
||||||
return h
|
return h
|
||||||
|
@ -83,7 +69,7 @@ func (c *OldCluster) FindHypervisorByName(name string) *Hypervisor {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) AddHypervisor(hostname string, cpus int, mem int) *Hypervisor {
|
func (c *Cluster) AddHypervisor(hostname string, cpus int, mem int) *Hypervisor {
|
||||||
h := c.FindHypervisorByName(hostname)
|
h := c.FindHypervisorByName(hostname)
|
||||||
if h != nil {
|
if h != nil {
|
||||||
return h
|
return h
|
||||||
|
@ -104,25 +90,15 @@ func (c *OldCluster) AddHypervisor(hostname string, cpus int, mem int) *Hypervis
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) AddEvent(e *Event) {
|
func (c *Cluster) AddEvent(e *Event) {
|
||||||
c.e.Events = append(c.e.Events, e)
|
c.e.Events = append(c.e.Events, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a new droplet with default values
|
func (c *Cluster) AddDroplet(d *Droplet) {
|
||||||
func NewDefaultDroplet(hostname string) *Droplet {
|
c.d.Droplets = append(c.d.Droplets, d)
|
||||||
id := uuid.New() // Generate a new UUID
|
|
||||||
d := &Droplet{
|
|
||||||
Uuid: id.String(),
|
|
||||||
Hostname: hostname,
|
|
||||||
Cpus: int64(2),
|
|
||||||
}
|
|
||||||
d.Memory = SetGB(2)
|
|
||||||
d.StartState = DropletState_OFF
|
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) AddDropletSimple(uuid string, hostname string, cpus int, mem int) *Droplet {
|
func (c *Cluster) AddDropletSimple(uuid string, hostname string, cpus int, mem int) *Droplet {
|
||||||
d := c.FindDropletByName(hostname)
|
d := c.FindDropletByName(hostname)
|
||||||
if d != nil {
|
if d != nil {
|
||||||
return d
|
return d
|
||||||
|
@ -144,7 +120,7 @@ func (c *OldCluster) AddDropletSimple(uuid string, hostname string, cpus int, me
|
||||||
}
|
}
|
||||||
|
|
||||||
// This isn't for the marketing department
|
// This isn't for the marketing department
|
||||||
func (c *OldCluster) AddDropletLocal(name string, hypername string) *Droplet {
|
func (c *Cluster) AddDropletLocal(name string, hypername string) *Droplet {
|
||||||
d := &Droplet{
|
d := &Droplet{
|
||||||
Hostname: name,
|
Hostname: name,
|
||||||
}
|
}
|
||||||
|
@ -162,10 +138,10 @@ func (c *OldCluster) AddDropletLocal(name string, hypername string) *Droplet {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) BlankFields() {
|
func (c *Cluster) BlankFields() {
|
||||||
loop := c.d.All() // get the list of droplets
|
loop := c.DropletsAll() // get the list of droplets
|
||||||
for loop.Scan() {
|
for loop.Scan() {
|
||||||
d := loop.Next()
|
d := loop.Droplet()
|
||||||
d.Current = nil
|
d.Current = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +150,7 @@ func (epb *Events) AppendEvent(e *Event) {
|
||||||
epb.Events = append(epb.Events, e)
|
epb.Events = append(epb.Events, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) ClusterStable() (bool, string) {
|
func (c *Cluster) ClusterStable() (bool, string) {
|
||||||
last := time.Since(c.Unstable.AsTime())
|
last := time.Since(c.Unstable.AsTime())
|
||||||
if last > c.UnstableTimeout.AsDuration() {
|
if last > c.UnstableTimeout.AsDuration() {
|
||||||
// the cluster has not been stable for 133 seconds
|
// the cluster has not been stable for 133 seconds
|
||||||
|
@ -186,7 +162,7 @@ func (c *OldCluster) ClusterStable() (bool, string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the cluster and droplet to make sure it's ready to start
|
// check the cluster and droplet to make sure it's ready to start
|
||||||
func (c *OldCluster) DropletReady(d *Droplet) (bool, string) {
|
func (c *Cluster) DropletReady(d *Droplet) (bool, string) {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return false, "cluster == nil"
|
return false, "cluster == nil"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
// thank chatgpt for this because why. why write this if you can have it
|
// thank chatgpt for this because why. why write this if you can have it
|
||||||
// kick this out in 30 seconds
|
// kick this out in 30 seconds
|
||||||
|
@ -14,7 +14,7 @@ func backupDir(srcDir string, destDir string) error {
|
||||||
// Create the destination directory
|
// Create the destination directory
|
||||||
err := os.MkdirAll(destDir, os.ModePerm)
|
err := os.MkdirAll(destDir, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to create directory: %v\n", err)
|
log.Println("Failed to create directory: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func backupDir(srcDir string, destDir string) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to copy files: %v\n", err)
|
log.Println("Failed to copy files: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
20
change.go
20
change.go
|
@ -1,4 +1,4 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "reflect"
|
// "reflect"
|
||||||
|
@ -34,6 +34,7 @@ func convertToAnypb(x any) *anypb.Any {
|
||||||
return a
|
return a
|
||||||
default:
|
default:
|
||||||
log.Error(errors.New("convertToAnypb() unknown type"), "v =", v, "x =", x)
|
log.Error(errors.New("convertToAnypb() unknown type"), "v =", v, "x =", x)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -64,6 +65,7 @@ func convertToString(x any) string {
|
||||||
default:
|
default:
|
||||||
log.Info("convertToSTring() unknown type", v)
|
log.Info("convertToSTring() unknown type", v)
|
||||||
log.Error(errors.New("convertToSTring() unknown type"), "v =", v, "x =", x)
|
log.Error(errors.New("convertToSTring() unknown type"), "v =", v, "x =", x)
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -73,7 +75,7 @@ func (d *Droplet) NewChangeEvent(fname string, origval any, newval any) *Event {
|
||||||
var e *Event
|
var e *Event
|
||||||
e = new(Event)
|
e = new(Event)
|
||||||
|
|
||||||
e.DropletName = d.Hostname
|
e.Droplet = d.Hostname
|
||||||
e.OrigVal = convertToString(origval)
|
e.OrigVal = convertToString(origval)
|
||||||
e.NewVal = convertToString(newval)
|
e.NewVal = convertToString(newval)
|
||||||
e.FieldName = fname
|
e.FieldName = fname
|
||||||
|
@ -99,12 +101,12 @@ func NewAddEvent(a any, fname string, newval any) *Event {
|
||||||
case *Droplet:
|
case *Droplet:
|
||||||
var d *Droplet
|
var d *Droplet
|
||||||
d = a.(*Droplet)
|
d = a.(*Droplet)
|
||||||
e.DropletName = d.Hostname
|
e.Droplet = d.Hostname
|
||||||
case nil:
|
case nil:
|
||||||
e.DropletName = "<nil>"
|
e.Droplet = "<nil>"
|
||||||
default:
|
default:
|
||||||
log.Info("newAddEvent() unknown type", v)
|
log.Info("newAddEvent() unknown type", v)
|
||||||
e.DropletName = "on something somewhere"
|
e.Droplet = "on something somewhere"
|
||||||
}
|
}
|
||||||
|
|
||||||
e.NewVal = convertToString(newval)
|
e.NewVal = convertToString(newval)
|
||||||
|
@ -163,7 +165,7 @@ func (d *Droplet) SetState(newState DropletState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// records an event that the droplet changed state (aka turned on, turned off, etc)
|
// records an event that the droplet changed state (aka turned on, turned off, etc)
|
||||||
func (c *OldCluster) ChangeDropletState(d *Droplet, newState DropletState) error {
|
func (c *Cluster) ChangeDropletState(d *Droplet, newState DropletState) error {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return errors.New("cluster is nil")
|
return errors.New("cluster is nil")
|
||||||
}
|
}
|
||||||
|
@ -177,7 +179,7 @@ func (c *OldCluster) ChangeDropletState(d *Droplet, newState DropletState) error
|
||||||
var e *Event
|
var e *Event
|
||||||
e = new(Event)
|
e = new(Event)
|
||||||
|
|
||||||
e.DropletName = d.Hostname
|
e.Droplet = d.Hostname
|
||||||
e.OrigVal = convertToString(d.Current.State)
|
e.OrigVal = convertToString(d.Current.State)
|
||||||
e.NewVal = convertToString(newState)
|
e.NewVal = convertToString(newState)
|
||||||
e.FieldName = "status"
|
e.FieldName = "status"
|
||||||
|
@ -190,7 +192,7 @@ func (c *OldCluster) ChangeDropletState(d *Droplet, newState DropletState) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// records an event that the droplet migrated to another hypervisor
|
// records an event that the droplet migrated to another hypervisor
|
||||||
func (c *OldCluster) DropletMoved(d *Droplet, newh *Hypervisor) error {
|
func (c *Cluster) DropletMoved(d *Droplet, newh *Hypervisor) error {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return errors.New("cluster is nil")
|
return errors.New("cluster is nil")
|
||||||
}
|
}
|
||||||
|
@ -209,7 +211,7 @@ func (c *OldCluster) DropletMoved(d *Droplet, newh *Hypervisor) error {
|
||||||
var e *Event
|
var e *Event
|
||||||
e = new(Event)
|
e = new(Event)
|
||||||
|
|
||||||
e.DropletName = d.Hostname
|
e.Droplet = d.Hostname
|
||||||
e.OrigVal = d.Current.Hypervisor
|
e.OrigVal = d.Current.Hypervisor
|
||||||
e.NewVal = newh.Hostname
|
e.NewVal = newh.Hostname
|
||||||
e.FieldName = "droplet migrate"
|
e.FieldName = "droplet migrate"
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package virtbuf
|
||||||
|
|
||||||
|
import (
|
||||||
|
sync "sync"
|
||||||
|
|
||||||
|
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cluster struct {
|
||||||
|
sync.RWMutex
|
||||||
|
|
||||||
|
Dirs []string
|
||||||
|
d *Droplets
|
||||||
|
H *Hypervisors
|
||||||
|
e *Events
|
||||||
|
Unstable *timestamppb.Timestamp
|
||||||
|
UnstableTimeout *durationpb.Duration
|
||||||
|
}
|
|
@ -1,23 +0,0 @@
|
||||||
syntax = "proto3";
|
|
||||||
package virtpb;
|
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto";
|
|
||||||
import "droplet.proto";
|
|
||||||
import "hypervisor.proto";
|
|
||||||
import "event.proto";
|
|
||||||
|
|
||||||
message Cluster { // `autogenpb:marshal`
|
|
||||||
string uuid = 1; // `autogenpb:unique`
|
|
||||||
string name = 2;
|
|
||||||
repeated string URL = 3;
|
|
||||||
google.protobuf.Timestamp ctime = 4; // when the cluster was created
|
|
||||||
Droplets droplets = 5;
|
|
||||||
Hypervisors hypervisors = 6;
|
|
||||||
Events events = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Clusters { // `autogenpb:marshal`
|
|
||||||
string uuid = 1; // `autogenpb:uuid:57ddd763-75f6-4003-bf0e-8dd0f8a44044`
|
|
||||||
string version = 2; // `autogenpb:version:v0.0.1`
|
|
||||||
repeated Cluster clusters = 3;
|
|
||||||
}
|
|
111
config.go
111
config.go
|
@ -1,4 +1,4 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
// functions to import and export the protobuf
|
// functions to import and export the protobuf
|
||||||
// data to and from config files
|
// data to and from config files
|
||||||
|
@ -9,37 +9,14 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
"google.golang.org/protobuf/encoding/prototext"
|
"google.golang.org/protobuf/encoding/prototext"
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Cluster) ConfigSave() error {
|
|
||||||
name := c.Name
|
|
||||||
if name == "" {
|
|
||||||
name = c.Uuid
|
|
||||||
}
|
|
||||||
fullname := filepath.Join(os.Getenv("VIRTIGO_HOME"), name+".pb")
|
|
||||||
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
|
||||||
defer cfgfile.Close()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("open config file :", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info("ConfigSave()", fullname)
|
|
||||||
data, err := c.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("cluster Marshal() err:", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintln(cfgfile, data)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writes out the cluster information it seperate files
|
// writes out the cluster information it seperate files
|
||||||
// to make it humanly possible to hand edit things as needed
|
// to make it humanly possible to hand edit things as needed
|
||||||
func (c *OldCluster) ConfigSave() error {
|
func (c *Cluster) ConfigSave() error {
|
||||||
// try to backup the current cluster config files
|
// try to backup the current cluster config files
|
||||||
if err := backupConfig(); err != nil {
|
if err := backupConfig(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -48,21 +25,31 @@ func (c *OldCluster) ConfigSave() error {
|
||||||
// make a new droplets struct
|
// make a new droplets struct
|
||||||
var dcopy *Droplets
|
var dcopy *Droplets
|
||||||
dcopy = new(Droplets)
|
dcopy = new(Droplets)
|
||||||
loop := c.d.All() // get the list of droplets
|
loop := c.DropletsAll() // get the list of droplets
|
||||||
for loop.Scan() {
|
for loop.Scan() {
|
||||||
d := loop.Next()
|
d := loop.Droplet()
|
||||||
dcopy.Droplets = append(dcopy.Droplets, d)
|
var newd Droplet
|
||||||
|
newd = *d
|
||||||
|
dcopy.Droplets = append(dcopy.Droplets, &newd)
|
||||||
}
|
}
|
||||||
// delete all the Current data so it's not put in the config file
|
// delete all the Current data so it's not put in the config file
|
||||||
for _, drop := range dcopy.Droplets {
|
for _, drop := range dcopy.Droplets {
|
||||||
drop.Current = nil
|
drop.Current = nil
|
||||||
}
|
}
|
||||||
|
if err := ConfigWriteJSON(dcopy, "droplets.json"); err != nil {
|
||||||
|
fmt.Println("droplets.json write failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := ConfigWriteTEXT(dcopy, "droplets.text"); err != nil {
|
if err := ConfigWriteTEXT(dcopy, "droplets.text"); err != nil {
|
||||||
fmt.Println("droplets.json write failed")
|
fmt.Println("droplets.json write failed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.configWriteDroplets()
|
c.configWriteDroplets()
|
||||||
|
|
||||||
|
if err := ConfigWriteJSON(c.H, "hypervisors.json"); err != nil {
|
||||||
|
fmt.Println("hypervisors.json write failed")
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := ConfigWriteTEXT(c.H, "hypervisors.text"); err != nil {
|
if err := ConfigWriteTEXT(c.H, "hypervisors.text"); err != nil {
|
||||||
fmt.Println("hypervisors.json write failed")
|
fmt.Println("hypervisors.json write failed")
|
||||||
return err
|
return err
|
||||||
|
@ -79,27 +66,27 @@ func (c *OldCluster) ConfigSave() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) ConfigLoad() error {
|
func (c *Cluster) ConfigLoad() error {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return errors.New("It's not safe to run ConfigLoad() on a nil cluster")
|
return errors.New("It's not safe to run ConfigLoad() on a nil cluster")
|
||||||
}
|
}
|
||||||
|
|
||||||
if data, err := loadFile("droplets.text"); err == nil {
|
if data, err := loadFile("droplets.json"); err == nil {
|
||||||
if err = prototext.Unmarshal(data, c.d); err != nil {
|
if err = protojson.Unmarshal(data, c.d); err != nil {
|
||||||
fmt.Println("broken droplets.text config file")
|
fmt.Println("broken droplets.json config file")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if data, err := loadFile("hypervisors.text"); err == nil {
|
if data, err := loadFile("hypervisors.json"); err == nil {
|
||||||
if err = prototext.Unmarshal(data, c.H); err != nil {
|
if err = protojson.Unmarshal(data, c.H); err != nil {
|
||||||
fmt.Println("broken hypervisors.text config file")
|
fmt.Println("broken hypervisors.json config file")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Warn("ERROR HERE IN Hypervisors")
|
fmt.Println("ERROR HERE IN Hypervisors")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +95,8 @@ func (c *OldCluster) ConfigLoad() error {
|
||||||
// does it not stay allocated after this function ends?
|
// does it not stay allocated after this function ends?
|
||||||
c.e = new(Events)
|
c.e = new(Events)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.e.loadEvents(); err != nil {
|
if err := c.e.loadEvents(); err != nil {
|
||||||
// ignore events.pb since these should be sent elsewhere
|
return err
|
||||||
log.Warn("Events failed to load, ignoring:", err)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -160,7 +144,7 @@ func loadFile(filename string) ([]byte, error) {
|
||||||
|
|
||||||
func ConfigWriteJSON(a any, filename string) error {
|
func ConfigWriteJSON(a any, filename string) error {
|
||||||
fullname := filepath.Join(os.Getenv("VIRTIGO_HOME"), filename)
|
fullname := filepath.Join(os.Getenv("VIRTIGO_HOME"), filename)
|
||||||
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
defer cfgfile.Close()
|
defer cfgfile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("open config file :", err)
|
fmt.Println("open config file :", err)
|
||||||
|
@ -175,26 +159,9 @@ func ConfigWriteJSON(a any, filename string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) configWriteDroplets() error {
|
|
||||||
fullname := filepath.Join(os.Getenv("VIRTIGO_HOME"), "droplets.new.text")
|
|
||||||
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
|
||||||
defer cfgfile.Close()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("open config file :", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
loop := c.d.All() // get the list of droplets
|
|
||||||
for loop.Scan() {
|
|
||||||
d := loop.Next()
|
|
||||||
text := prototext.Format(d)
|
|
||||||
fmt.Fprintln(cfgfile, text)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConfigWriteTEXT(a any, filename string) error {
|
func ConfigWriteTEXT(a any, filename string) error {
|
||||||
fullname := filepath.Join(os.Getenv("VIRTIGO_HOME"), filename)
|
fullname := filepath.Join(os.Getenv("VIRTIGO_HOME"), filename)
|
||||||
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
defer cfgfile.Close()
|
defer cfgfile.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("open config file :", err)
|
fmt.Println("open config file :", err)
|
||||||
|
@ -209,18 +176,22 @@ func ConfigWriteTEXT(a any, filename string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Clusters) ConfigLoad() error {
|
func (c *Cluster) configWriteDroplets() error {
|
||||||
if c == nil {
|
fullname := filepath.Join(os.Getenv("VIRTIGO_HOME"), "droplets.new.text")
|
||||||
return errors.New("It's not safe to run ConfigLoad() on a nil cluster")
|
cfgfile, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
}
|
defer cfgfile.Close()
|
||||||
|
if err != nil {
|
||||||
if data, err := loadFile("cluster.text"); err == nil {
|
fmt.Println("open config file :", err)
|
||||||
if err = prototext.Unmarshal(data, c); err != nil {
|
|
||||||
fmt.Println("broken cluster.textconfig file")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
loop := c.DropletsAll() // get the list of droplets
|
||||||
|
for loop.Scan() {
|
||||||
|
d := loop.Droplet()
|
||||||
|
var newd Droplet
|
||||||
|
newd = *d
|
||||||
|
newd.Current = nil
|
||||||
|
text := prototext.Format(&newd)
|
||||||
|
fmt.Fprintln(cfgfile, text)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
// thank chatgpt for this because why. why write this if you can have it
|
// thank chatgpt for this because why. why write this if you can have it
|
||||||
// kick this out in 30 seconds
|
// kick this out in 30 seconds
|
||||||
|
|
|
@ -1,82 +1,81 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package virtpb;
|
package virtbuf;
|
||||||
|
|
||||||
import "google/protobuf/duration.proto"; // Import the well-known type for Timestamp
|
import "google/protobuf/duration.proto"; // Import the well-known type for Timestamp
|
||||||
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
||||||
|
|
||||||
// global settings for autogenpb `autogenpb:mutex`
|
message Droplets {
|
||||||
|
string uuid = 1; // I guess why not just have this on each file
|
||||||
message Droplets { // `autogenpb:marshal` `autogenpb:gui`
|
string version = 2; // maybe can be used for protobuf schema change violations
|
||||||
string uuid = 1; // `autogenpb:uuid:d5d492e2-38d4-476b-86f3-f5abf01f9d6d`
|
repeated Droplet droplets = 3;
|
||||||
string version = 2; // `autogenpb:version:v0.0.1`
|
|
||||||
repeated Droplet droplets = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Droplet { // `autogenpb:marshal`
|
message Droplet {
|
||||||
string uuid = 1; // `autogenpb:unique` // should be unique across the cluster
|
string uuid = 1; // should be unique across the cluster
|
||||||
string hostname = 2; // `autogenpb:unique` // should be unique and work in DNS
|
string hostname = 2; // should be unique and work in DNS
|
||||||
int64 cpus = 3; // what's the point of int64 vs int32
|
int64 cpus = 3; // what's the point of int64 vs int32
|
||||||
int64 memory = 4; // in bytes
|
int64 memory = 4; // in bytes
|
||||||
Current current = 5; // what the state and values of the droplet is
|
Current current = 5; // what the state and values of the droplet is
|
||||||
DropletState startState = 6; // what the state of the droplet is SUPPOSED TO BE ('on' or 'off')
|
DropletState start_state = 6; // what the state of the droplet is SUPPOSED TO BE ('on' or 'off')
|
||||||
string qemuMachine = 7; // the qemu machine type to use "pc-q35-9.0"
|
string qemu_machine = 7; // the qemu machine type to use "pc-q35-9.0"
|
||||||
int64 spicePort = 8; // preferred port to use for spice
|
int64 spice_port = 8; // preferred port to use for spice
|
||||||
|
|
||||||
string preferredHypervisor = 9; // the hypervisor to prefer to run the droplet on
|
string preferred_hypervisor = 9; // the hypervisor to prefer to run the droplet on
|
||||||
string forceHypervisor = 10; // use this hypervisor and this hypervisor only
|
string force_hypervisor = 10; // use this hypervisor and this hypervisor only
|
||||||
string preferredArch = 11; // the cpu arch to use "x86_64" (should really get this from the disk?)
|
string preferred_arch = 11; // the cpu arch to use "x86_64" (should really get this from the disk?)
|
||||||
repeated Network networks = 12; // really just mac addresses. should be unique across cluster
|
repeated Network networks = 12; // really just mac addresses. should be unique across cluster
|
||||||
repeated Disk disks = 13; // disks to attach
|
repeated Disk disks = 13; // disks to attach
|
||||||
|
|
||||||
string localOnly = 14; // this is only defined locally on the hypervisor
|
string local_only = 14; // this is only defined locally on the hypervisor
|
||||||
string customXml = 15; // if needed,
|
string custom_xml = 15; // if needed,
|
||||||
Archive archive = 16; // what the state of the droplet is SUPPOSED TO BE ('on' or 'off')
|
Archive archive = 16; // what the state of the droplet is SUPPOSED TO BE ('on' or 'off')
|
||||||
|
|
||||||
google.protobuf.Timestamp unstable = 39; // the last time we heard anything from this droplet
|
google.protobuf.Timestamp unstable = 39; // the last time we heard anything from this droplet
|
||||||
google.protobuf.Duration unstableTimeout = 40; // the last time we heard anything from this droplet
|
google.protobuf.Duration unstable_timeout = 40; // the last time we heard anything from this droplet
|
||||||
}
|
}
|
||||||
|
|
||||||
// volatile data. the current settings and values of things.
|
// volatile data. the current settings and values of things.
|
||||||
// These are passed around while the cluster to monitor and control the systems
|
// These are passed around while the cluster to monitor and control the systems
|
||||||
// but they are not saved to the config file
|
// but they are not saved to the config file
|
||||||
message Current {
|
message Current {
|
||||||
DropletState state = 1; // used to track the current state before taking any action
|
DropletState state = 1; // used to track the current state before taking any action
|
||||||
string hypervisor = 2; // the current hypervisor the droplet is running on
|
string hypervisor = 2; // the current hypervisor the droplet is running on
|
||||||
int64 startAttempts = 3; // how many times a start has been attempted
|
int64 start_attempts = 3; // how many times a start has been attempted
|
||||||
string fullXml = 4; // the full libvirt xml to import
|
string full_xml = 4; // the full libvirt xml to import
|
||||||
google.protobuf.Timestamp lastPoll = 5; // the last time we heard anything from this droplet
|
google.protobuf.Timestamp last_poll = 5; // the last time we heard anything from this droplet
|
||||||
string imageUrl = 6; // url to the image
|
string image_url = 6; // url to the image
|
||||||
google.protobuf.Timestamp offSince = 7; // when the droplet was turned off
|
google.protobuf.Timestamp off_since = 7; // when the droplet was turned off
|
||||||
google.protobuf.Timestamp onSince = 8; // when the droplet was turned on
|
google.protobuf.Timestamp on_since = 8; // when the droplet was turned on
|
||||||
}
|
}
|
||||||
|
|
||||||
message Archive {
|
message Archive {
|
||||||
DropletArchive reason = 1; // why the droplet was archived
|
DropletArchive reason = 1; // why the droplet was archived
|
||||||
google.protobuf.Timestamp when = 2; // when it was archived
|
google.protobuf.Timestamp when = 2; // when it was archived
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual machine state
|
||||||
enum DropletState {
|
enum DropletState {
|
||||||
ON = 0;
|
ON = 0;
|
||||||
OFF = 1;
|
OFF = 1;
|
||||||
UNKNOWN = 2; // qemu says 'Shutdown'
|
UNKNOWN = 2; // qemu says 'Shutdown'
|
||||||
PAUSED = 3;
|
PAUSED = 3;
|
||||||
CRASHED = 4;
|
CRASHED = 4;
|
||||||
INMIGRATE = 5;
|
INMIGRATE = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DropletArchive {
|
enum DropletArchive {
|
||||||
DUP = 0;
|
DUP = 0;
|
||||||
USER = 1;
|
USER = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Network {
|
message Network {
|
||||||
string mac = 1;
|
string mac = 1;
|
||||||
string name = 2;
|
string name = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Disk {
|
message Disk {
|
||||||
string filename = 1;
|
string filename = 1;
|
||||||
string filepath = 2;
|
string filepath = 2;
|
||||||
int64 size = 3;
|
int64 size = 3;
|
||||||
string qemuArch = 4; // what arch. example: "x86_64" or "riscv64"
|
string qemu_arch = 4; // what arch. example: "x86_64" or "riscv64"
|
||||||
}
|
}
|
||||||
|
|
79
event.proto
79
event.proto
|
@ -1,17 +1,14 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package virtpb;
|
package virtbuf;
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
||||||
import "google/protobuf/any.proto"; // Import the well-known type for Timestamp
|
import "google/protobuf/any.proto"; // Import the well-known type for Timestamp
|
||||||
import "droplet.proto";
|
|
||||||
|
|
||||||
// global settings for autogenpb `autogenpb:no-sort` `autogenpb:mutex`
|
message Events {
|
||||||
|
string uuid = 1; // I guess why not just have this on each file
|
||||||
message Events { // `autogenpb:marshal` `autogenpb:gui`
|
string version = 2; // maybe can be used for protobuf schema change violations
|
||||||
string uuid = 1; // `autogenpb:uuid:1e3a50c7-5916-4423-b33c-f0b977a7e446`
|
int64 event_size = 3; // max events to store in a single
|
||||||
string version = 2; // `autogenpb:version:v0.0.1`
|
repeated Event events = 4; // all the events
|
||||||
int64 eventSize = 3; // max events to store in a single
|
|
||||||
repeated Event events = 4; // all the events
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this information leans towards being human readable not programatic
|
// this information leans towards being human readable not programatic
|
||||||
|
@ -19,42 +16,34 @@ message Events { // `autogenpb:marsh
|
||||||
// at least for now in the early days. but maybe forever.
|
// at least for now in the early days. but maybe forever.
|
||||||
// homelab clouds normally don't have many events.
|
// homelab clouds normally don't have many events.
|
||||||
// we are talking less than 1 a minute. even 1 an hour is often a lot
|
// we are talking less than 1 a minute. even 1 an hour is often a lot
|
||||||
message Event { // `autogenpb:marshal`
|
message Event {
|
||||||
enum status {
|
int32 id = 1;
|
||||||
DONE = 0;
|
EventType etype = 2;
|
||||||
FAIL = 1;
|
string droplet = 3; // name of the droplet
|
||||||
RUNNING = 2;
|
string droplet_uuid = 4; // uuid of the droplet
|
||||||
}
|
string hypervisor = 5; // name of the hypervisor
|
||||||
int32 id = 1; // `autogenpb:unique` // should be unique across the cluster
|
string hypervisor_uuid = 6; // uuid of the hypervisor
|
||||||
EventType etype = 2;
|
google.protobuf.Timestamp start = 7; // start time
|
||||||
string dropletName = 3; // name of the droplet
|
google.protobuf.Timestamp end = 8; // end time
|
||||||
string dropletUuid = 4; // uuid of the droplet
|
string field_name = 9; // the field name that changed
|
||||||
string hypervisor = 5; // name of the hypervisor
|
string orig_val = 10; // original value
|
||||||
string hypervisorUuid = 6; // uuid of the hypervisor
|
string new_val = 11; // new value
|
||||||
google.protobuf.Timestamp start = 7; // start time
|
google.protobuf.Any orig_any = 12; // anypb format. probably overkill
|
||||||
google.protobuf.Timestamp end = 8; // end time
|
google.protobuf.Any new_any = 13; // anypb format
|
||||||
string fieldName = 9; // the field name that changed
|
|
||||||
string origVal = 10; // original value
|
|
||||||
string newVal = 11; // new value
|
|
||||||
google.protobuf.Any origAny = 12; // anypb format. probably overkill
|
|
||||||
google.protobuf.Any newAny = 13; // anypb format
|
|
||||||
string error = 14; // what went wrong
|
|
||||||
status state = 15; // state of the event
|
|
||||||
Droplet droplet = 16; // droplet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EventType {
|
enum EventType {
|
||||||
ADD = 0;
|
ADD = 0;
|
||||||
DELETE = 1;
|
DELETE = 1;
|
||||||
POWERON = 2;
|
POWERON = 2;
|
||||||
POWEROFF = 3; // should indicate a "normal" shutdown
|
POWEROFF = 3; // should indicate a "normal" shutdown
|
||||||
HIBERNATE = 4;
|
HIBERNATE = 4;
|
||||||
MIGRATE = 5;
|
MIGRATE = 5;
|
||||||
DEMO = 6;
|
DEMO = 6;
|
||||||
GET = 7; // request something
|
GET = 7; // request something
|
||||||
LOGIN = 8; // attempt to login
|
LOGIN = 8; // attempt to login
|
||||||
OK = 9; // everything is ok
|
OK = 9; // everything is ok
|
||||||
FAIL = 10; // everything failed
|
FAIL = 10; // everything failed
|
||||||
CRASH = 11; // droplet hard crashed
|
CRASH = 11; // droplet hard crashed
|
||||||
CHANGE = 12; // droplet or hypervisor config change
|
CHANGE = 12; // droplet or hypervisor config change
|
||||||
EDIT = 13; // edit droplet settings
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
build:
|
||||||
|
GO111MODULE=off go build
|
||||||
|
./example
|
||||||
|
|
||||||
|
goimports:
|
||||||
|
goimports -w *.go
|
||||||
|
|
||||||
|
prep:
|
||||||
|
go get -v -t -u
|
||||||
|
|
||||||
|
run:
|
||||||
|
go run *.go
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -f example
|
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
pb "go.wit.com/lib/protobuf/virtbuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// saves entries in a config file
|
||||||
|
//
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
TestWriteCluster()
|
||||||
|
|
||||||
|
var c *pb.Cluster
|
||||||
|
c = pb.InitCluster()
|
||||||
|
|
||||||
|
// log.Println(aCluster.String())
|
||||||
|
// show the droplets to STDOUT
|
||||||
|
loop := c.DropletsAll() // get the list of droplets
|
||||||
|
for loop.Scan() {
|
||||||
|
d := loop.Droplet()
|
||||||
|
fmt.Println("\tdroplet =", d.Hostname, "preffered host:", d.PreferredHypervisor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// show the hypervisors to STDOUT
|
||||||
|
for _, h := range aCluster.Hypervisors {
|
||||||
|
fmt.Println("\thypervisor =", h.Hostname, h.GetMemoryPrintable())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
json := aCluster.FormatJSON()
|
||||||
|
fmt.Println(json)
|
||||||
|
|
||||||
|
data, _ := aCluster.MarshalJSON()
|
||||||
|
fmt.Println(string(data))
|
||||||
|
|
||||||
|
text := aCluster.FormatTEXT()
|
||||||
|
fmt.Println(text)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func marshalWriteToFile(myWriter *bufio.Writer, c *pb.Cluster) {
|
||||||
|
buf, err := proto.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("marshaling error: ", err)
|
||||||
|
}
|
||||||
|
tmp, err := myWriter.Write(buf)
|
||||||
|
myWriter.Flush()
|
||||||
|
log.Println("bufio.Write() tmp, err = ", tmp, err)
|
||||||
|
|
||||||
|
buf, err = proto.Marshal(c)
|
||||||
|
tmp2, err := myWriter.Write(buf)
|
||||||
|
myWriter.Flush()
|
||||||
|
log.Println("bufio.Write() tmp2, err = ", tmp2, err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestWriteCluster() {
|
||||||
|
c := pb.CreateSampleCluster(7)
|
||||||
|
os.Setenv("VIRTIGO_HOME", "/tmp/virtigo/")
|
||||||
|
|
||||||
|
if err := c.ConfigSave(); err != nil {
|
||||||
|
fmt.Println("configsave error", err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
// marshalUnmarshal()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func marshalUnmarshal() {
|
||||||
|
test := pb.CreateSampleCluster(7)
|
||||||
|
data, err := proto.Marshal(test)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("marshaling error: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newTest := &pb.Cluster{}
|
||||||
|
err = proto.Unmarshal(data, newTest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unmarshaling error: ", err)
|
||||||
|
} else {
|
||||||
|
log.Println("proto.Marshal() and proto.Unmarshal() worked")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,19 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package virtbuf;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
|
||||||
|
import "google/protobuf/any.proto"; // Import the well-known type for Timestamp
|
||||||
|
|
||||||
|
message WhatsThis {
|
||||||
|
// is it possible to have custom formatting in JSON and TEXT marshal/unmarshal ?
|
||||||
|
WhatInfo humantest = 1;
|
||||||
|
|
||||||
|
google.protobuf.Timestamp end = 2; // end time
|
||||||
|
google.protobuf.Any orig_val = 3; // original value
|
||||||
|
google.protobuf.Any new_val = 4; // new value
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is for exerimenting
|
||||||
|
message WhatInfo {
|
||||||
|
int64 capacity = 1; // Stores the storage capacity in bytes.
|
||||||
|
}
|
76
helpers.go
76
helpers.go
|
@ -1,17 +1,81 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
// functions to import and export the protobuf
|
// functions to import and export the protobuf
|
||||||
// data to and from config files
|
// data to and from config files
|
||||||
|
|
||||||
func InitCluster() *OldCluster {
|
import (
|
||||||
var c *OldCluster
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
c = new(OldCluster)
|
"google.golang.org/protobuf/encoding/prototext"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitCluster() *Cluster {
|
||||||
|
var c *Cluster
|
||||||
|
c = new(Cluster)
|
||||||
c.d = new(Droplets)
|
c.d = new(Droplets)
|
||||||
c.H = new(Hypervisors)
|
c.H = new(Hypervisors)
|
||||||
c.e = new(Events)
|
c.e = new(Events)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) DropletsAll() *DropletScanner {
|
// human readable JSON
|
||||||
return c.d.All()
|
func (d *Droplets) FormatJSON() string {
|
||||||
|
return protojson.Format(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Droplet) FormatJSON() string {
|
||||||
|
return protojson.Format(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) FormatJSON() string {
|
||||||
|
return protojson.Format(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hypervisors) FormatJSON() string {
|
||||||
|
return protojson.Format(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// apparently this isn't supposed to be used?
|
||||||
|
// https://protobuf.dev/reference/go/faq/#unstable-text
|
||||||
|
// this is a shame because this is much nicer output than JSON Format()
|
||||||
|
func (d *Droplets) FormatTEXT() string {
|
||||||
|
return prototext.Format(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Droplet) FormatTEXT() string {
|
||||||
|
return prototext.Format(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) FormatTEXT() string {
|
||||||
|
return prototext.Format(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshal
|
||||||
|
func (d *Droplets) MarshalJSON() ([]byte, error) {
|
||||||
|
return protojson.Marshal(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Droplet) MarshalJSON() ([]byte, error) {
|
||||||
|
return protojson.Marshal(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) MarshalJSON() ([]byte, error) {
|
||||||
|
return protojson.Marshal(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal
|
||||||
|
func (d *Droplets) UnmarshalJSON(data []byte) error {
|
||||||
|
return protojson.Unmarshal(data, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Droplet) UnmarshalJSON(data []byte) error {
|
||||||
|
return protojson.Unmarshal(data, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) UnmarshalJSON(data []byte) error {
|
||||||
|
return protojson.Unmarshal(data, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Droplet) Unmarshal(data []byte) error {
|
||||||
|
return proto.Unmarshal(data, d)
|
||||||
}
|
}
|
||||||
|
|
6
human.go
6
human.go
|
@ -1,4 +1,4 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
// mostly just functions related to making STDOUT
|
// mostly just functions related to making STDOUT
|
||||||
// more readable by us humans
|
// more readable by us humans
|
||||||
|
@ -97,7 +97,7 @@ func FormatDuration(d time.Duration) string {
|
||||||
ms := int(d.Milliseconds())
|
ms := int(d.Milliseconds())
|
||||||
if ms > 100 {
|
if ms > 100 {
|
||||||
// todo: print .3s, etc ?
|
// todo: print .3s, etc ?
|
||||||
return fmt.Sprintf("%1.2fs", float64(seconds)/1000)
|
return fmt.Sprintf("%1.2fs", seconds/1000)
|
||||||
}
|
}
|
||||||
if ms > 0 {
|
if ms > 0 {
|
||||||
result += fmt.Sprintf("%dms", ms)
|
result += fmt.Sprintf("%dms", ms)
|
||||||
|
@ -185,7 +185,7 @@ func (d *Droplet) DumpDroplet(w http.ResponseWriter, r *http.Request) (string, e
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OldCluster) DumpDroplet(w http.ResponseWriter, r *http.Request) (string, error) {
|
func (c *Cluster) DumpDroplet(w http.ResponseWriter, r *http.Request) (string, error) {
|
||||||
hostname := r.URL.Query().Get("hostname")
|
hostname := r.URL.Query().Get("hostname")
|
||||||
d := c.FindDropletByName(hostname)
|
d := c.FindDropletByName(hostname)
|
||||||
if d == nil {
|
if d == nil {
|
||||||
|
|
|
@ -1,29 +1,26 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package virtpb;
|
package virtbuf;
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto";
|
message Hypervisors {
|
||||||
|
string uuid = 1; // I guess why not just have this on each file
|
||||||
message Hypervisors { // `autogenpb:marshal` `autogenpb:gui`
|
string version = 2; // maybe can be used for protobuf schema change violations
|
||||||
string uuid = 1; // `autogenpb:uuid:6e3aa8b9-cf98-40f6-af58-3c6ad1edf4d4`
|
repeated Hypervisor hypervisors = 3;
|
||||||
string version = 2; // `autogenpb:version:v0.0.1`
|
|
||||||
repeated Hypervisor hypervisors = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Hypervisor {
|
message Hypervisor {
|
||||||
string uuid = 1; // `autogenpb:unique`
|
string uuid = 1;
|
||||||
string hostname = 2; // `autogenpb:unique`
|
string hostname = 2;
|
||||||
bool active = 3; // is allowed to start new droplets
|
bool active = 3; // is allowed to start new droplets
|
||||||
int64 cpus = 4;
|
int64 cpus = 4;
|
||||||
int64 memory = 5; // in bytes
|
int64 memory = 5; // in bytes
|
||||||
string comment = 6;
|
string comment = 6;
|
||||||
bool autoscan = 7; // to scan or not to scan by virtigo
|
bool autoscan = 7; // to scan or not to scan by virtigo
|
||||||
HypervisorArch arch = 8;
|
HypervisorArch arch = 8;
|
||||||
int64 killcount = 9; // in bytes
|
|
||||||
google.protobuf.Timestamp lastPoll = 10; // the last time we heard anything
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// think about this more
|
||||||
enum HypervisorArch {
|
enum HypervisorArch {
|
||||||
RISCV64 = 0;
|
RISCV64 = 0;
|
||||||
X86_64 = 1;
|
X86_64 = 1;
|
||||||
ARM64 = 2;
|
ARM64 = 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
package virtpb
|
|
||||||
|
|
||||||
import (
|
|
||||||
sync "sync"
|
|
||||||
|
|
||||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OldCluster struct {
|
|
||||||
sync.RWMutex
|
|
||||||
|
|
||||||
Dirs []string
|
|
||||||
d *Droplets
|
|
||||||
H *Hypervisors
|
|
||||||
e *Events
|
|
||||||
Unstable *timestamppb.Timestamp
|
|
||||||
UnstableTimeout *durationpb.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OldCluster) GetDropletsPB() *Droplets {
|
|
||||||
return c.d
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OldCluster) GetHypervisorsPB() *Hypervisors {
|
|
||||||
return c.H
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *OldCluster) GetEventsPB() *Events {
|
|
||||||
return c.e
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds a new droplet. enforce unique hostnames
|
|
||||||
func (c *OldCluster) AddDroplet(newd *Droplet) bool {
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
for _, d := range c.d.Droplets {
|
|
||||||
if newd.Hostname == d.Hostname {
|
|
||||||
// boo. that one is already here
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// everything is ok, this hostname is new
|
|
||||||
c.d.Droplets = append(c.d.Droplets, newd)
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -39,6 +39,22 @@ func CreateSampleHypervisor(hostname string, mem int) *Hypervisor {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateExperiment(total int) *WhatsThis {
|
||||||
|
var e *WhatsThis
|
||||||
|
e = new(WhatsThis)
|
||||||
|
|
||||||
|
// info := StorageInfo{Capacity: 64}
|
||||||
|
// e.Humantest = &info
|
||||||
|
if e.Humantest == nil {
|
||||||
|
var newInfo WhatInfo
|
||||||
|
newInfo = WhatInfo{Capacity: 64}
|
||||||
|
e.Humantest = &newInfo
|
||||||
|
} else {
|
||||||
|
e.Humantest.Capacity = SetGB(total * 32)
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
func CreateSampleEvents(total int) *Events {
|
func CreateSampleEvents(total int) *Events {
|
||||||
var e *Events
|
var e *Events
|
||||||
e = new(Events)
|
e = new(Events)
|
||||||
|
@ -51,7 +67,7 @@ func CreateSampleEvents(total int) *Events {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSampleCluster(total int) *OldCluster {
|
func CreateSampleCluster(total int) *Cluster {
|
||||||
c := InitCluster()
|
c := InitCluster()
|
||||||
|
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
package virtbuf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DropletIterator struct {
|
||||||
|
droplets []*Droplet
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDropletIterator initializes a new iterator.
|
||||||
|
func NewDropletIterator(droplets []*Droplet) *DropletIterator {
|
||||||
|
return &DropletIterator{droplets: droplets}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan moves to the next element and returns false if there are no more droplets.
|
||||||
|
func (it *DropletIterator) Scan() bool {
|
||||||
|
if it.index >= len(it.droplets) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.index++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Droplet returns the current droplet.
|
||||||
|
func (it *DropletIterator) Droplet() *Droplet {
|
||||||
|
if it.droplets[it.index-1] == nil {
|
||||||
|
for i, d := range it.droplets {
|
||||||
|
fmt.Println("i =", i, d)
|
||||||
|
}
|
||||||
|
fmt.Println("len =", len(it.droplets))
|
||||||
|
fmt.Println("droplet == nil", it.index, it.index-1)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
return it.droplets[it.index-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Scan() in a loop, similar to a while loop
|
||||||
|
//
|
||||||
|
// for iterator.Scan() {
|
||||||
|
// d := iterator.Droplet()
|
||||||
|
// fmt.Println("Droplet UUID:", d.Uuid)
|
||||||
|
// }
|
||||||
|
func (c *Cluster) GetDropletIterator() *DropletIterator {
|
||||||
|
dropletPointers := c.SelectDropletPointers()
|
||||||
|
|
||||||
|
iterator := NewDropletIterator(dropletPointers)
|
||||||
|
|
||||||
|
return iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) DropletsAll() *DropletIterator {
|
||||||
|
dropletPointers := c.SelectDropletAll()
|
||||||
|
|
||||||
|
iterator := NewDropletIterator(dropletPointers)
|
||||||
|
|
||||||
|
return iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectDropletPointers safely returns a slice of pointers to Droplet records.
|
||||||
|
func (c *Cluster) SelectDropletAll() []*Droplet {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
|
||||||
|
// Create a new slice to hold pointers to each Droplet
|
||||||
|
// dropletPointers := make([]*Droplet, len(c.E.Droplets))
|
||||||
|
var dropletPointers []*Droplet
|
||||||
|
if c.d == nil {
|
||||||
|
log.Info("SelectDropletsAll() c.d == nil")
|
||||||
|
// os.Exit(-1)
|
||||||
|
}
|
||||||
|
for _, d := range c.d.Droplets {
|
||||||
|
if d == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if d.Archive != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dropletPointers = append(dropletPointers, d) // Copy pointers for safe iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
return dropletPointers
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectDropletPointers safely returns a slice of pointers to Droplet records.
|
||||||
|
func (c *Cluster) SelectDropletPointers() []*Droplet {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
|
||||||
|
// Create a new slice to hold pointers to each Droplet
|
||||||
|
// dropletPointers := make([]*Droplet, len(c.E.Droplets))
|
||||||
|
dropletPointers := make([]*Droplet, 1)
|
||||||
|
if c.d == nil {
|
||||||
|
log.Info("c.d == nil")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
for _, d := range c.d.Droplets {
|
||||||
|
dropletPointers = append(dropletPointers, d) // Copy pointers for safe iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
return dropletPointers
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
package virtpb
|
package virtbuf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
// MarshalJSON custom marshals the WhatInfo struct to JSON
|
// MarshalJSON custom marshals the WhatInfo struct to JSON
|
||||||
func (s WhatInfo) MarshalJSON() ([]byte, error) {
|
func (s WhatInfo) MarshalJSON() ([]byte, error) {
|
||||||
capacityStr := fmt.Sprintf("%d GB", s.Capacity)
|
capacityStr := fmt.Sprintf("%d GB", s.Capacity)
|
||||||
|
@ -32,6 +37,7 @@ func (s *WhatInfo) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func main() {
|
func main() {
|
||||||
info := WhatInfo{Capacity: 64}
|
info := WhatInfo{Capacity: 64}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 29947bbd165cd2034df6d15ae06feb83af40c572
|
Loading…
Reference in New Issue